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依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- 引入阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- 模板引擎 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.5.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
</dependencies>
2.spring-security的配置
(1)认证类
springsecurity自定义认证规则,用户的登录就会走这个类去认证,并且在这里进行角色权限的赋予
/**
* description: 用户认证 <br>
* date: 2021/8/1 15:14 <br>
* author: ztz <br>
* version: 1.0 <br>
*/
@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<User>().eq("user_name", username));
if(user==null){<!-- -->
return null;
}else {<!-- -->
Collection<GrantedAuthority> authorities = new ArrayList<>();
// 数据库中查询出所拥有的角色进行赋予
for (Role role : userMapper.findRoleByUsername(username)) {<!-- -->
authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
}
return new JwtUser(user,authorities);
}
}
}
(2)自定义加密规则
可以自定义Encoder规则,只要实现PasswordEncoder这个接口并且在配置类中使用这个加密类就好
/**
* description: 自定义加密规则 <br>
* date: 2021/8/1 18:22 <br>
* author: ztz <br>
* version: 1.0 <br>
* @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配置 <br>
* date: 2021/7/31 18:15 <br>
* author: ztz <br>
* version: 1.0 <br>
*/
@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的授予 <br>
* date: 2021/8/5 13:19 <br>
* author: ztz <br>
* version: 1.0 <br>
*/
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {<!-- -->
private ThreadLocal<Integer> rememberMe = new ThreadLocal<>();
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<>())
);
} 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<? extends GrantedAuthority> 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时的验证 <br>
* date: 2021/8/5 13:20 <br>
* author: ztz <br>
* version: 1.0 <br>
*/
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<GrantedAuthority> authorities = new ArrayList<>();
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;
}
}
正文完