赞
踩
springboot版本:3.2.0
jdk版本:21
security,跟随boot版本。
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
Security6相比Security5及之前的版本,在配置上做出了断层式的改变。和之前继承WebSecurityConfigurerAdapter相比,现在种种配置都是以bean的形式呈现的。
- @Slf4j
- @EnableWebSecurity
- @Configuration
- @EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
- public class SecurityConfig {
-
- // 白名单
- @Value("#{'${config.security.white-list}'.split(',')}")
- private String[] whiteList;
-
- @Resource
- private AuthenticationTokenFilter authenticationTokenFilter;
-
- @Bean
- public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
- return httpSecurity
- .authorizeHttpRequests(req -> req.requestMatchers(whiteList).permitAll()
- .anyRequest().authenticated())
- .exceptionHandling(http -> http.authenticationEntryPoint(failureHandling()))
- // token过滤器
- .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
- .csrf(AbstractHttpConfigurer::disable)
- .cors(AbstractHttpConfigurer::disable)
- // 无状态
- .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
- .build();
- }
-
- @Bean
- public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
- return authenticationConfiguration.getAuthenticationManager();
- }
-
- /**
- * 强散列哈希加密实现
- */
- @Bean
- public PasswordEncoder bCryptPasswordEncoder() {
- return new BCryptPasswordEncoder();
- }
-
- /**
- * 验证失败处理器
- *
- */
- public AuthenticationEntryPoint failureHandling() {
- return (request, response, exception) -> {
- log.error(exception.getMessage());
- RespEnum resultEnum = RespEnum.UNAUTHORIZED;
- exception.printStackTrace();
- Resp<Void> resp = Resp.error(resultEnum);
- response.setContentType("application/json");
- response.setCharacterEncoding("utf-8");
- response.getWriter().write(JSON.toJSONString(resp));
- };
- }
- }
这里用过滤器的方式验证用户,用户信息放到请求上下文中
- @Component
- public class AuthenticationTokenFilter extends OncePerRequestFilter {
-
- @Resource
- private TokenService tokenService;
-
-
- @Override
- public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
- String token = SecurityUtils.getToken(request);
- // 无token不加载用户信息
- if (Func.isEmpty(token) || token.startsWith("Basic")) {
- chain.doFilter(request, response);
- return;
- }
- // 解析token,并从redis中取出loginUser
- LoginUser loginUser = tokenService.verifyToken(token);
- // security的认证实际上最终操作的就是这个authenticationToken
- UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
- authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
- SecurityContextHolder.getContext().setAuthentication(authenticationToken);
- chain.doFilter(request, response);
- }
- }
tokenService不变(主要是对token、用户的处理)。
- @Component
- public class TokenService {
-
- @Resource
- private RedisTemplate<String, String> redisTemplate;
-
- // 令牌默认存储时长
- protected static final long EXPIRE_SECOND = 3600;
- // token在redis中的key
- private final static String ACCESS_TOKEN = "login_tokens:";
-
- /**
- * 创建令牌
- */
- public String createToken(LoginUser loginUser) {
- String token = IdUtil.nanoId();
- // 存储
- refreshToken(loginUser, token);
- return token;
- }
-
-
- /**
- * 获取用户身份信息
- *
- * @return 用户信息
- */
- public LoginUser getLoginUser(String token) {
- if (StringUtils.isNotEmpty(token)) {
- String tokenKey = getTokenKey(token);
- String userStr = redisTemplate.opsForValue().get(tokenKey);
- LoginUser loginUser = JSON.parseObject(userStr, LoginUser.class);
- return loginUser;
- }
- return null;
- }
-
- /**
- * 删除用户缓存信息
- */
- public void removeToken(String token) {
- if (token == null) {
- return;
- }
- String tokenKey = getTokenKey(token);
- redisTemplate.delete(tokenKey);
- }
-
- /**
- * 验证令牌有效期,相差不足120分钟,自动刷新缓存
- *
- * @param token
- */
- public LoginUser verifyToken(String token) {
- LoginUser loginUser = getLoginUser(token);
- // 刷新token
- refreshToken(loginUser, token);
- return loginUser;
- }
-
- /**
- * 刷新令牌有效期
- *
- * @param loginUser 登录信息
- */
- public void refreshToken(LoginUser loginUser, String token) {
- // 将loginUser缓存
- String userKey = getTokenKey(token);
- ValueOperations<String, String> forValue = redisTemplate.opsForValue();
- forValue.set(userKey, JSON.toJSONString(loginUser) , Duration.ofSeconds(EXPIRE_SECOND));
- }
-
- private String getTokenKey(String token) {
- return ACCESS_TOKEN + token;
- }
- }
登录不变,还是原先的,核心代码:
- @Resource
- private AuthenticationManager authenticationManager;
-
- public Resp<String> login(@Valid @RequestBody LoginReqDTO loginBody) {
- String username = loginBody.getUsername();
- String password = loginBody.getPassword();
- UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
- Authentication authenticate = authenticationManager.authenticate(authenticationToken);
- LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
- String token = tokenService.createToken(loginUser);
- return Resp.success(token);
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。