springsecurity 配置

1、框架介绍

Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization两个部分。
(1)用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。
(2)用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般
  来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
Spring Security其实就是用filter,多请求的路径进行过滤。
(1)如果是基于Session,那么Spring-security会对cookie里的sessionid进行解析,找到服务器存储的sesion信息,然后判断当前用户是否符合请求的要求。
(2)如果是token,则是解析出token,然后将当前请求加入到Spring-security管理的权限信息中去

2认证与授权实现思路

如果系统的模块众多,每个模块都需要就行授权与认证,所以我们选择基于token的形式进行授权与认证,用户根据用户名密码认证成功,然后获取当前用户角色的一系列权限值,并以用户名为
key,权限列表为value的形式存入redis缓存中,根据用户名相关信息生成token返回,浏览器将token记录到cookie中,每次调用api接口都默认将token携带到header请求头中,Spring-security解析
header头获取token信息,解析token获取当前用户名,根据用户名就可以从redis中获取权限列表,这样Spring-security就能够判断当前请求是否有权限访问

3. 配置与使用

1.依赖引入

    <dependencies>
        <dependency>
            <groupId>com.atguigu</groupId>
            <artifactId>common-util</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- Spring Security依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
    </dependencies>
pom

2. 各类功能介绍

 1. security

 1 import com.atguigu.commonutils.MD5;
 2 import org.springframework.security.crypto.password.PasswordEncoder;
 3 import org.springframework.stereotype.Component;
 4 
 5 /**
 6  * <p>
 7  * t密码的处理方法类型
 8  * </p>
 9  *
10  */
11 @Component
12 public class DefaultPasswordEncoder implements PasswordEncoder {
13 
14     public DefaultPasswordEncoder() {
15         this(-1);
16     }
17 
18     /**
19      * @param strength
20      *            the log rounds to use, between 4 and 31
21      */
22     public DefaultPasswordEncoder(int strength) {
23 
24     }
25 
26     public String encode(CharSequence rawPassword) {
27         return MD5.encrypt(rawPassword.toString());
28     }
29 
30     public boolean matches(CharSequence rawPassword, String encodedPassword) {
31         return encodedPassword.equals(MD5.encrypt(rawPassword.toString()));
32     }
33 }
DefaultPasswordEncoder
 1 import com.atguigu.commonutils.R;
 2 import com.atguigu.commonutils.ResponseUtil;
 3 import org.springframework.data.redis.core.RedisTemplate;
 4 import org.springframework.security.core.Authentication;
 5 import org.springframework.security.web.authentication.logout.LogoutHandler;
 6 
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 /**
11  * <p>
12  * 登出业务逻辑类
13  * </p>
14  *
15  */
16 public class TokenLogoutHandler implements LogoutHandler {
17 
18     private TokenManager tokenManager;
19     private RedisTemplate redisTemplate;
20 
21     public TokenLogoutHandler(TokenManager tokenManager, RedisTemplate redisTemplate) {
22         this.tokenManager = tokenManager;
23         this.redisTemplate = redisTemplate;
24     }
25 
26     @Override
27     public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
28         String token = request.getHeader("token");
29         if (token != null) {
30             tokenManager.removeToken(token); // 该行其实没有处理
31 
32             //清空当前用户缓存中的权限数据
33             String userName = tokenManager.getUserFromToken(token);
34             redisTemplate.delete(userName);
35         }
36 
37         ResponseUtil.out(response, R.ok());
38     }
39 
40 }
TokenLogoutHandler
 1 import io.jsonwebtoken.CompressionCodecs;
 2 import io.jsonwebtoken.Jwts;
 3 import io.jsonwebtoken.SignatureAlgorithm;
 4 import org.springframework.stereotype.Component;
 5 
 6 import java.util.Date;
 7 
 8 /**
 9  * <p>
10  * token管理
11  * </p>
12  *
13  */
14 @Component
15 public class TokenManager {
16 
17     private long tokenExpiration = 24*60*60*1000;
18     private String tokenSignKey = "123456";
19 
20     public String createToken(String username) {
21         String token = Jwts.builder().setSubject(username)
22                 .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
23                 .signWith(SignatureAlgorithm.HS512, tokenSignKey)
24                 .compressWith(CompressionCodecs.GZIP)
25                 .compact();
26         return token;
27     }
28 
29     public String getUserFromToken(String token) {
30         String user = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getSubject();
31         return user;
32     }
33 
34     public void removeToken(String token) {
35         //jwttoken无需删除,客户端扔掉即可。
36     }
37 
38 }
TokenManager
 1 import com.atguigu.commonutils.R;
 2 import com.atguigu.commonutils.ResponseUtil;
 3 import org.springframework.security.core.AuthenticationException;
 4 import org.springframework.security.web.AuthenticationEntryPoint;
 5 
 6 import javax.servlet.ServletException;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 import java.io.IOException;
10 
11 /**
12  * <p>
13  * 未授权的统一处理方式
14  * </p>
15  *
16  */
17 public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
18 
19     @Override
20     public void commence(HttpServletRequest request, HttpServletResponse response,
21                          AuthenticationException authException) throws IOException, ServletException {
22 
23         ResponseUtil.out(response, R.error());
24     }
25 }
UnauthorizedEntryPoint

2.filter

 1 import com.atguigu.commonutils.R;
 2 import com.atguigu.commonutils.ResponseUtil;
 3 import com.atguigu.serurity.security.TokenManager;
 4 import org.springframework.data.redis.core.RedisTemplate;
 5 import org.springframework.security.authentication.AuthenticationManager;
 6 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 7 import org.springframework.security.core.GrantedAuthority;
 8 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 9 import org.springframework.security.core.context.SecurityContextHolder;
10 import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
11 import org.springframework.util.StringUtils;
12 
13 import javax.servlet.FilterChain;
14 import javax.servlet.ServletException;
15 import javax.servlet.http.HttpServletRequest;
16 import javax.servlet.http.HttpServletResponse;
17 import java.io.IOException;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.List;
21 
22 /**
23  * <p>
24  * 访问过滤器
25  * </p>
26  *
27  */
28 public class TokenAuthenticationFilter extends BasicAuthenticationFilter {
29     private TokenManager tokenManager;
30     private RedisTemplate redisTemplate;
31 
32     public TokenAuthenticationFilter(AuthenticationManager authManager, TokenManager tokenManager,RedisTemplate redisTemplate) {
33         super(authManager);
34         this.tokenManager = tokenManager;
35         this.redisTemplate = redisTemplate;
36     }
37 
38     @Override
39     protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
40             throws IOException, ServletException {
41         // System.out.println("=================<"+req.getRequestURI());
42         // System.out.println("===============token:" + req.getHeader("token"));
43         if(req.getRequestURI().indexOf("admin") == -1) {
44             chain.doFilter(req, res);
45             return;
46         }
47 
48         UsernamePasswordAuthenticationToken authentication = null;
49         try {
50             authentication = getAuthentication(req);
51         } catch (Exception e) {
52             ResponseUtil.out(res, R.error());
53         }
54 
55         if (authentication != null) {
56             System.out.println(authentication.getPrincipal());
57             SecurityContextHolder.getContext().setAuthentication(authentication);
58         } else {
59             ResponseUtil.out(res, R.error());
60         }
61         chain.doFilter(req, res);
62     }
63 
64     private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
65         // token置于header里
66         String token = request.getHeader("token");
67         // System.out.println("token=======>" + token);
68         // token不为空或空字符串
69         if (token != null && !"".equals(token.trim())) {
70             String userName = tokenManager.getUserFromToken(token);
71 
72             List<String> permissionValueList = (List<String>) redisTemplate.opsForValue().get(userName);
73             Collection<GrantedAuthority> authorities = new ArrayList<>();
74             for(String permissionValue : permissionValueList) {
75                 if(StringUtils.isEmpty(permissionValue)) continue;
76                 SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
77                 authorities.add(authority);
78             }
79 
80 
81             if (!StringUtils.isEmpty(userName)) {
82 
83                 return new UsernamePasswordAuthenticationToken(userName, token, authorities);
84             }
85             return null;
86         }
87         return null;
88     }
89 }
TokenAuthenticationFilter
 1 import com.atguigu.commonutils.R;
 2 import com.atguigu.commonutils.ResponseUtil;
 3 import com.atguigu.serurity.entity.SecurityUser;
 4 import com.atguigu.serurity.entity.User;
 5 import com.atguigu.serurity.security.TokenManager;
 6 import com.fasterxml.jackson.databind.ObjectMapper;
 7 import org.springframework.data.redis.core.RedisTemplate;
 8 import org.springframework.security.authentication.AuthenticationManager;
 9 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
10 import org.springframework.security.core.Authentication;
11 import org.springframework.security.core.AuthenticationException;
12 import org.springframework.security.core.context.SecurityContextHolder;
13 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
14 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
15 
16 import javax.servlet.FilterChain;
17 import javax.servlet.ServletException;
18 import javax.servlet.http.Cookie;
19 import javax.servlet.http.HttpServletRequest;
20 import javax.servlet.http.HttpServletResponse;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 
24 /**
25  * <p>
26  * 登录过滤器,继承UsernamePasswordAuthenticationFilter,对用户名密码进行登录校验
27  * </p>
28  *
29  */
30 public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
31 
32     private AuthenticationManager authenticationManager;
33     private TokenManager tokenManager;
34     private RedisTemplate redisTemplate;
35 
36     public TokenLoginFilter(AuthenticationManager authenticationManager, TokenManager tokenManager, RedisTemplate redisTemplate) {
37         this.authenticationManager = authenticationManager;
38         this.tokenManager = tokenManager;
39         this.redisTemplate = redisTemplate;
40         this.setPostOnly(false);
41         AntPathRequestMatcher requestMatcher = new AntPathRequestMatcher("/admin/acl/login", "POST");
42         System.out.println(requestMatcher);
43         this.setRequiresAuthenticationRequestMatcher(requestMatcher);
44 
45     }
46 
47     @Override
48     public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
49             throws AuthenticationException {
50         try {
51             User user = new ObjectMapper().readValue(req.getInputStream(), User.class);
52             System.out.println(user);
53             UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>());
54             return authenticationManager.authenticate(usernamePasswordAuthenticationToken);
55         } catch (IOException e) {
56             throw new RuntimeException(e);
57         }
58 
59     }
60 
61     /**
62      * 登录成功
63      * @param req
64      * @param res
65      * @param chain
66      * @param auth
67      * @throws IOException
68      * @throws ServletException
69      */
70     @Override
71     protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain,
72                                             Authentication auth) throws IOException, ServletException {
73 
74         SecurityUser user = (SecurityUser) auth.getPrincipal();
75         String token = tokenManager.createToken(user.getCurrentUserInfo().getUsername());
76         redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(), user.getPermissionValueList());
77         res.setHeader("token",token);
78         // System.out.println("token:" + token);
79         ResponseUtil.out(res, R.ok().data("token", token));
80     }
81 
82     /**
83      * 登录失败
84      * @param request
85      * @param response
86      * @param e
87      * @throws IOException
88      * @throws ServletException
89      */
90     @Override
91     protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
92                                               AuthenticationException e) throws IOException, ServletException {
93         ResponseUtil.out(response, R.error());
94     }
95 }
TokenLoginFilter

3. entity

 1 import lombok.Data;
 2 import lombok.extern.slf4j.Slf4j;
 3 import org.springframework.security.core.GrantedAuthority;
 4 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 5 import org.springframework.security.core.userdetails.UserDetails;
 6 import org.springframework.util.StringUtils;
 7 
 8 import java.util.ArrayList;
 9 import java.util.Collection;
10 import java.util.List;
11 
12 /**
13  * <p>
14  * 安全认证用户详情信息
15  * </p>
16  *
17  */
18 @Data
19 @Slf4j
20 public class SecurityUser implements UserDetails {
21 
22     //当前登录用户 transient不被序列化
23     private transient User currentUserInfo;
24 
25     //当前权限
26     private List<String> permissionValueList;
27 
28     public SecurityUser() {
29     }
30 
31     public SecurityUser(User user) {
32         if (user != null) {
33             this.currentUserInfo = user;
34         }
35     }
36 
37     @Override
38     public Collection<? extends GrantedAuthority> getAuthorities() {
39         Collection<GrantedAuthority> authorities = new ArrayList<>();
40         for(String permissionValue : permissionValueList) {
41             if(StringUtils.isEmpty(permissionValue)) continue;
42             SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
43             authorities.add(authority);
44         }
45 
46         return authorities;
47     }
48 
49     @Override
50     public String getPassword() {
51         return currentUserInfo.getPassword();
52     }
53 
54     @Override
55     public String getUsername() {
56         return currentUserInfo.getUsername();
57     }
58 
59     @Override
60     public boolean isAccountNonExpired() {
61         return true;
62     }
63 
64     @Override
65     public boolean isAccountNonLocked() {
66         return true;
67     }
68 
69     @Override
70     public boolean isCredentialsNonExpired() {
71         return true;
72     }
73 
74     @Override
75     public boolean isEnabled() {
76         return true;
77     }
78 }
SecurityUser
 1 import io.swagger.annotations.ApiModel;
 2 import io.swagger.annotations.ApiModelProperty;
 3 import lombok.Data;
 4 
 5 import java.io.Serializable;
 6 
 7 /**
 8  * <p>
 9  * 用户实体类
10  * </p>
11  *
12  */
13 @Data
14 @ApiModel(description = "用户实体类")
15 public class User implements Serializable {
16 
17     private static final long serialVersionUID = 1L;
18 
19     @ApiModelProperty(value = "微信openid")
20     private String username;
21 
22     @ApiModelProperty(value = "密码")
23     private String password;
24 
25     @ApiModelProperty(value = "昵称")
26     private String nickName;
27 
28     @ApiModelProperty(value = "用户头像")
29     private String salt;
30 
31     @ApiModelProperty(value = "用户签名")
32     private String token;
33 
34 }
User

4. config

 1 import com.atguigu.serurity.filter.TokenAuthenticationFilter;
 2 import com.atguigu.serurity.filter.TokenLoginFilter;
 3 import com.atguigu.serurity.security.DefaultPasswordEncoder;
 4 import com.atguigu.serurity.security.TokenLogoutHandler;
 5 import com.atguigu.serurity.security.TokenManager;
 6 import com.atguigu.serurity.security.UnauthorizedEntryPoint;
 7 import org.springframework.beans.factory.annotation.Autowired;
 8 import org.springframework.context.annotation.Configuration;
 9 import org.springframework.data.redis.core.RedisTemplate;
10 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
11 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
12 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
13 import org.springframework.security.config.annotation.web.builders.WebSecurity;
14 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
15 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
16 import org.springframework.security.core.userdetails.UserDetailsService;
17 
18 /**
19  * <p>
20  * Security配置类
21  * </p>
22  *
23  */
24 @Configuration
25 @EnableWebSecurity
26 @EnableGlobalMethodSecurity(prePostEnabled = true)
27 public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {
28 
29     private UserDetailsService userDetailsService;
30     private TokenManager tokenManager;
31     private DefaultPasswordEncoder defaultPasswordEncoder;
32     private RedisTemplate redisTemplate;
33 
34     @Autowired
35     public TokenWebSecurityConfig(UserDetailsService userDetailsService, DefaultPasswordEncoder defaultPasswordEncoder,
36                                   TokenManager tokenManager, RedisTemplate redisTemplate) {
37         this.userDetailsService = userDetailsService;
38         this.defaultPasswordEncoder = defaultPasswordEncoder;
39         this.tokenManager = tokenManager;
40         this.redisTemplate = redisTemplate;
41     }
42 
43     /**
44      * 配置设置
45      * @param http
46      * @throws Exception
47      */
48     @Override
49     protected void configure(HttpSecurity http) throws Exception {
50         http.exceptionHandling()
51                 .authenticationEntryPoint(new UnauthorizedEntryPoint())
52                 .and().csrf().disable()
53                 .authorizeRequests()
54                 .antMatchers("/js/**","/css/**","/images/**","/fonts/**","/**/*.png","/**/*.jpg").permitAll()
55                 .antMatchers("/swagger-ui.html/**","/api/**","/admin/**").permitAll()
56                 .anyRequest().authenticated()
57                 .and().logout().logoutUrl("/admin/acl/index/logout")
58                 .addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate)).and()
59                 .addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, redisTemplate))
60                 .addFilter(new TokenAuthenticationFilter(authenticationManager(), tokenManager, redisTemplate)).httpBasic();
61     }
62 
63     /**
64      * 密码处理
65      * @param auth
66      * @throws Exception
67      */
68     @Override
69     public void configure(AuthenticationManagerBuilder auth) throws Exception {
70         auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);
71     }
72 
73     /**
74      * 配置哪些请求不拦截
75      * @param web
76      * @throws Exception
77      */
78     @Override
79     public void configure(WebSecurity web) throws Exception {
80        web.ignoring().antMatchers("/api/**",
81                "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**"
82               );
83         // 所以都不拦截
84         // web.ignoring().antMatchers("/*/**");
85     }
86 }
TokenWebSecurityConfig

5. 需要查询数据库所获得的用户信息接口UserDetailsService 的实现类

 1 import com.atguigu.aclservice.entity.User;
 2 import com.atguigu.aclservice.service.PermissionService;
 3 import com.atguigu.aclservice.service.UserService;
 4 import com.atguigu.serurity.entity.SecurityUser;
 5 import org.springframework.beans.BeanUtils;
 6 import org.springframework.beans.factory.annotation.Autowired;
 7 import org.springframework.security.core.userdetails.UserDetails;
 8 import org.springframework.security.core.userdetails.UserDetailsService;
 9 import org.springframework.security.core.userdetails.UsernameNotFoundException;
10 import org.springframework.stereotype.Service;
11 
12 import java.util.List;
13 
14 
15 /**
16  * <p>
17  * 自定义userDetailsService - 认证用户详情
18  * </p>
19  *
20  */
21 @Service("userDetailsService")
22 public class UserDetailsServiceImpl implements UserDetailsService {
23 
24     @Autowired
25     private UserService userService;
26 
27     @Autowired
28     private PermissionService permissionService;
29 
30     /***
31      * 根据账号获取用户信息
32      * @param username:
33      * @return: org.springframework.security.core.userdetails.UserDetails
34      */
35     @Override
36     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
37         // 从数据库中取出用户信息
38         User user = userService.selectByUsername(username);
39         System.out.println("user:" + user);
40 
41         // 判断用户是否存在
42         if (null == user){
43             //throw new UsernameNotFoundException("用户名不存在!");
44         }
45         // 返回UserDetails实现类
46         com.atguigu.serurity.entity.User curUser = new com.atguigu.serurity.entity.User();
47         BeanUtils.copyProperties(user,curUser);
48 
49         List<String> authorities = permissionService.selectPermissionValueByUserId(user.getId());
50         System.out.println("下面:" + authorities.get(0));
51         SecurityUser securityUser = new SecurityUser(curUser);
52         securityUser.setPermissionValueList(authorities);
53         return securityUser;
54     }
55 
56 }
UserDetailsServiceImpl
作者:zhangshuai
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
原文地址:https://www.cnblogs.com/zhangshaui/p/15062530.html