​springsecurity配合token进行权限控制

springsecurity配合token进行权限控制

Gitee地址:https://gitee.com/tansty/springboot-permission-control

参考别人的博客:https://blog.csdn.net/u012702547/article/details/89629415

一、spring security 简介

spring security 的核心功能主要包括:

  • 认证 (你是谁)- 授权 (你能干什么)- 攻击防护 (防止伪造身份)原理图

<img src=”https://img-blog.csdnimg.cn/172aca5367cc408b8072054d904b331d.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3RhbnN0eV96aA==,size_16,color_FFFFFF,t_70″ alt=”在这里插入图片描述”>

二、代码

1.项目maven依赖

&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-data-jdbc&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-configuration-processor&lt;/artifactId&gt;
&lt;optional&gt;true&lt;/optional&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;mysql&lt;/groupId&gt;
&lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
&lt;scope&gt;runtime&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.alibaba&lt;/groupId&gt;
&lt;artifactId&gt;druid-spring-boot-starter&lt;/artifactId&gt;
&lt;version&gt;1.1.10&lt;/version&gt;
&lt;/dependency&gt;
&lt;!-- 引入阿里数据库连接池 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.alibaba&lt;/groupId&gt;
&lt;artifactId&gt;druid&lt;/artifactId&gt;
&lt;version&gt;1.1.21&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
&lt;artifactId&gt;lombok&lt;/artifactId&gt;
&lt;optional&gt;true&lt;/optional&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
&lt;artifactId&gt;spring-security-test&lt;/artifactId&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;!--mybatis-plus--&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.baomidou&lt;/groupId&gt;
&lt;artifactId&gt;mybatis-plus-boot-starter&lt;/artifactId&gt;
&lt;version&gt;3.4.1&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.baomidou&lt;/groupId&gt;
&lt;artifactId&gt;mybatis-plus-generator&lt;/artifactId&gt;
&lt;version&gt;3.4.1&lt;/version&gt;
&lt;/dependency&gt;
&lt;!-- 模板引擎 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.apache.velocity&lt;/groupId&gt;
&lt;artifactId&gt;velocity-engine-core&lt;/artifactId&gt;
&lt;version&gt;2.0&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.aspectj&lt;/groupId&gt;
&lt;artifactId&gt;aspectjweaver&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;ma.glasnost.orika&lt;/groupId&gt;
&lt;artifactId&gt;orika-core&lt;/artifactId&gt;
&lt;version&gt;1.5.2&lt;/version&gt;
&lt;/dependency&gt;
&lt;!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 --&gt;
&lt;dependency&gt;
&lt;groupId&gt;io.springfox&lt;/groupId&gt;
&lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;
&lt;version&gt;2.9.2&lt;/version&gt;
&lt;/dependency&gt;
&lt;!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui --&gt;
&lt;dependency&gt;
&lt;groupId&gt;io.springfox&lt;/groupId&gt;
&lt;artifactId&gt;springfox-swagger-ui&lt;/artifactId&gt;
&lt;version&gt;2.9.2&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;io.jsonwebtoken&lt;/groupId&gt;
&lt;artifactId&gt;jjwt&lt;/artifactId&gt;
&lt;version&gt;0.9.1&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.codehaus.jackson&lt;/groupId&gt;
&lt;artifactId&gt;jackson-mapper-asl&lt;/artifactId&gt;
&lt;version&gt;1.9.13&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.alibaba&lt;/groupId&gt;
&lt;artifactId&gt;fastjson&lt;/artifactId&gt;
&lt;version&gt;1.2.75&lt;/version&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;

2.spring-security的配置

(1)认证类

springsecurity自定义认证规则,用户的登录就会走这个类去认证,并且在这里进行角色权限的赋予

/**
* description: 用户认证 &lt;br&gt;
* date: 2021/8/1 15:14 &lt;br&gt;
* author: ztz &lt;br&gt;
* version: 1.0 &lt;br&gt;
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {<!-- -->
@Autowired
UserMapper userMapper;
@Autowired
MyPasswordEncoder passwordEncoder ;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {<!-- -->
// 根据用户名去数据库中查询
User user = userMapper.selectOne(new QueryWrapper&lt;User&gt;().eq("user_name", username));
if(user==null){<!-- -->
return null;
}else {<!-- -->
Collection&lt;GrantedAuthority&gt; authorities = new ArrayList&lt;&gt;();
// 数据库中查询出所拥有的角色进行赋予
for (Role role : userMapper.findRoleByUsername(username)) {<!-- -->
authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
}
return new JwtUser(user,authorities);
}
}
}

(2)自定义加密规则

可以自定义Encoder规则,只要实现PasswordEncoder这个接口并且在配置类中使用这个加密类就好

/**
* description: 自定义加密规则 &lt;br&gt;
* date: 2021/8/1 18:22 &lt;br&gt;
* author: ztz &lt;br&gt;
* version: 1.0 &lt;br&gt;
* @author Tansty
*/
@Component
public class MyPasswordEncoder implements PasswordEncoder {<!-- -->
// 加密
@Override
public String encode(CharSequence rawPassword) {<!-- -->
return MD5Util.encode((String) rawPassword) ;
}
// 匹配
@Override
public boolean matches(CharSequence rawPassword, String encodePassword) {<!-- -->
return (MD5Util.encode( (String) rawPassword)).equals(encodePassword);
}
}

(3)核心配置类

/**
* description: Security配置 &lt;br&gt;
* date: 2021/7/31 18:15 &lt;br&gt;
* author: ztz &lt;br&gt;
* version: 1.0 &lt;br&gt;
*/
@Configuration
@EnableWebSecurity
//开启方法权限注解支持
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {<!-- -->
@Autowired
UserDetailsServiceImpl userDetailsService;
/**
* @Description 认证
* @author Tansty
* @date 2021/8/3 14:14
* @param auth
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {<!-- -->
auth.userDetailsService(userDetailsService).passwordEncoder(new MyPasswordEncoder());
}
/**
* @Description 配置静态资源的过滤
* @author Tansty
* @date 2021/8/3 14:14
* @param web
*/
@Override
public void configure(WebSecurity web) throws Exception {<!-- -->
// 配置静态文件不需要认证
web.ignoring().antMatchers(
"/swagger-ui.html",
// swagger api json
"/v2/api-docs",
// 用来获取支持的动作
"/swagger-resources/configuration/ui",
// 用来获取api-docs的URI
"/swagger-resources",
// 安全选项
"/swagger-resources/configuration/security",
"/swagger-resources/**",
"/webjars/**",
"/druid/**"
);
}
/**
* @Description 授权
* @author Tansty
* @date 2021/8/3 14:14
* @param http
*/
@Override
protected void configure(HttpSecurity http) throws Exception {<!-- -->
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/auth/login").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// 不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint())
// 添加无权限时的处理
.accessDeniedHandler(new JWTAccessDeniedHandler());
}
}

(4)token的拦截器

1.用户登录及token的授予
/**
* description: 用户登录及token的授予 &lt;br&gt;
* date: 2021/8/5 13:19 &lt;br&gt;
* author: ztz &lt;br&gt;
* version: 1.0 &lt;br&gt;
*/
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {<!-- -->
private ThreadLocal&lt;Integer&gt; rememberMe = new ThreadLocal&lt;&gt;();
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {<!-- -->
this.authenticationManager = authenticationManager;
// 在这里可以自定义登录的路径
super.setFilterProcessesUrl("/auth/login");
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {<!-- -->
// 从输入流中获取到登录的信息
try {<!-- -->
LoginUser loginUser = new ObjectMapper().readValue(request.getInputStream(), LoginUser.class);
//rememberMe.set(loginUser.getRememberMe() == null ? 0 : loginUser.getRememberMe());
System.out.println(loginUser);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList&lt;&gt;())
);
} catch (IOException e) {<!-- -->
e.printStackTrace();
return null;
}
}
// 成功验证后调用的方法
// 如果验证成功,就生成token并返回
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authResult) throws IOException, ServletException {<!-- -->
JwtUser jwtUser = (JwtUser) authResult.getPrincipal();
System.out.println("jwtUser:" + jwtUser.toString());
String role = "";
Collection&lt;? extends GrantedAuthority&gt; authorities = jwtUser.getAuthorities();
for (GrantedAuthority authority : authorities){<!-- -->
role = authority.getAuthority();
System.out.println(role);
}
String token = JwtTokenUtils.createToken(jwtUser.getUsername(), role, 0);
System.out.println(token);
// 返回创建成功的token
// 但是这里创建的token只是单纯的token
// 按照jwt的规定,最后请求的时候应该是 `Bearer token`
//RedisUtil redisUtil = new RedisUtil();
//redisUtil.set(jwtUser.getUsername(),JwtTokenUtils.TOKEN_PREFIX + token);
//redisUtil.expire(jwtUser.getUsername(),JwtTokenUtils.EXPIRATION);
response.setHeader("token", JwtTokenUtils.TOKEN_PREFIX + token);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {<!-- -->
response.getWriter().write("authentication failed, reason: " + failed.getMessage());
}
}

2.用户携带token时的验证
/**
* description: 用户携带token时的验证 &lt;br&gt;
* date: 2021/8/5 13:20 &lt;br&gt;
* author: ztz &lt;br&gt;
* version: 1.0 &lt;br&gt;
*/
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {<!-- -->
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {<!-- -->
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {<!-- -->
String tokenHeader = request.getHeader(JwtTokenUtils.TOKEN_HEADER);
// 如果请求头中没有Authorization信息则直接放行了
if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)) {<!-- -->
chain.doFilter(request, response);
return;
}
// 如果请求头中有token,则进行解析,并且设置认证信息
try {<!-- -->
SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));
} catch (TokenIsExpiredException e) {<!-- -->
//返回json形式的错误信息
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
String reason = "统一处理,原因:" + e.getMessage();
response.getWriter().write(new ObjectMapper().writeValueAsString(reason));
response.getWriter().flush();
return;
}
super.doFilterInternal(request, response, chain);
}
// 这里从token中获取用户信息并新建一个token
private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) throws TokenIsExpiredException {<!-- -->
String token = tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, "");
System.out.println(token);
boolean expiration = JwtTokenUtils.isExpiration(token);
if (expiration) {<!-- -->
throw new TokenIsExpiredException("token超时了");
} else {<!-- -->
String username = JwtTokenUtils.getUsername(token);
// redis集成
//RedisUtil redisUtil = new RedisUtil();
//String rToken = null;
//try {<!-- -->
// rToken = (String) redisUtil.get(username);
//} catch (Exception e) {<!-- -->
// e.printStackTrace();
// return null;
//}
//if(!(rToken.equals(tokenHeader))){<!-- -->
// return null;
//}
String role = JwtTokenUtils.getUserRole(token);
if (username != null) {<!-- -->
Collection&lt;GrantedAuthority&gt; authorities = new ArrayList&lt;&gt;();
UserMapper userMapper = SpringUtil.getBean(UserMapper.class);
authorities.add(new SimpleGrantedAuthority(role));
// 数据库中查出所具有的权限进行授权
for (Permission permission : userMapper.findPermissionByUsername(username)) {<!-- -->
authorities.add(new SimpleGrantedAuthority(permission.getPermTag()));
}
return new UsernamePasswordAuthenticationToken(username, null,
authorities
);
}
}
return null;
}
}

正文完