美文网首页
SpringBoot整合SpringSecurity实现Toke

SpringBoot整合SpringSecurity实现Toke

作者: 任未然 | 来源:发表于2022-01-11 14:25 被阅读0次

    一. 概述

    SpringSecurity默认用过session保存用户登录状态, 现在都是分布式微服务时代了, 基本都是用token认证了,本demo简单实践下整合SpringSecurity实现Token认证

    二. SpringBootDemo

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
    

    2.1 实现思路

    1. 屏蔽默认的登录、登出逻辑
    2. 手写一套登入登出逻辑,登录成功把用户信息存到缓存里并设定有效期,登出则清除缓存
    3. 写一个过滤器,用来拦截校验请求

    2.2 自定义登录登出逻辑: AuthController.java

    @Slf4j
    @RestController
    @RequestMapping("/api/auth")
    public class AuthController {
    
        @Resource
        private AuthenticationManager authenticationManager;
    
        @Resource
        private RedisTemplate<String,Object> redisTemplate;
    
        /**
         * 登录
         */
        @PostMapping("/login")
        public ApiResponse login(@Valid @RequestBody LoginRequest loginRequest) {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginRequest.getUsernameOrEmailOrPhone(), loginRequest.getPassword());
            // 尝试对传递的Authentication对象进行身份Authentication ,如果成功,则返回完全填充的Authentication对象(包括授予的权限)。
            Authentication authentication = authenticationManager.authenticate(authenticationToken);
            // 把authentication放到当前线程,便是认证完成
            SecurityContextHolder.getContext().setAuthentication(authentication);
            String fastUUID = IdUtil.fastUUID();
            // 把用户信息存到redis,并设置有效期
            redisTemplate.opsForValue().set(fastUUID,authentication.getPrincipal(),6, TimeUnit.HOURS);
            return ApiResponse.ofSuccess(Dict.create().set("tokenType","Bearer").set("token",fastUUID));
        }
    
        /**
         * 登出
         * @param request
         * @return
         */
        @PostMapping("/logout")
        public ApiResponse logout(HttpServletRequest request) {
            try {
                // 清除认证信息
                String authorization = getAuthorization(request);
                SecurityContextHolder.clearContext();
                redisTemplate.delete(authorization);
            } catch (SecurityException e) {
                throw new SecurityException(Status.UNAUTHORIZED);
            }
            return ApiResponse.ofStatus(Status.LOGOUT);
        }
    
        /**
         * 从 request 的 header 中获取 Authorization
         *
         * @param request 请求
         * @return JWT
         */
        public String getAuthorization(HttpServletRequest request) {
            String bearerToken = request.getHeader("Authorization");
            if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith("Bearer ")) {
                return bearerToken.substring(7);
            }else {
                throw new SecurityException(-1,"Token非法");
            }
        }
    }
    

    2.3 Token认证过滤器: TokenAuthenticationFilter.java

    @Component
    @Slf4j
    public class TokenAuthenticationFilter extends OncePerRequestFilter {
    
        @Resource
        private RedisTemplate<String,Object> redisTemplate;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
            try {
                // 获取AuthorizationToken
                String authorization = getAuthorization(request);
                // 从缓存中获取用户信息
                UserDetails userDetails = (UserDetails)redisTemplate.opsForValue().get(authorization);
                Assert.notNull(userDetails,"登录已过期请重新登录");
    
                // 构建AuthenticationToken
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
    
                // 把AuthenticationToken放到当前线程,表示认证完成
                SecurityContextHolder.getContext().setAuthentication(authentication);
                filterChain.doFilter(request, response);
            } catch (SecurityException e) {
                ResponseUtil.renderJson(response, e);
            }
    
        }
    
        /**
         * 从 request 的 header 中获取 Authorization
         *
         * @param request 请求
         * @return JWT
         */
        public String getAuthorization(HttpServletRequest request) {
            String bearerToken = request.getHeader("Authorization");
            if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith("Bearer ")) {
                return bearerToken.substring(7);
            }else {
                throw new SecurityException(-1,"Token非法");
            }
        }
    
    }
    

    2.4 认证配置 SecurityConfig.java

    @Configuration
    @EnableWebSecurity
    @EnableConfigurationProperties(CustomConfig.class)
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private AccessDeniedHandler accessDeniedHandler;
    
        @Autowired
        private UserDetailsService userDetailsService;
    
        @Autowired
        private TokenAuthenticationFilter tokenAuthenticationFilter;
    
        @Bean
        public BCryptPasswordEncoder encoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        @Bean
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    
            // @formatter:off
            http.cors()
                    // 关闭 CSRF
                    .and().csrf().disable()
                    // 登录行为由自己实现,参考 AuthController#login
                    .formLogin().disable()
                    .httpBasic().disable()
    
                    // 认证请求
                    .authorizeRequests()
                    // 所有请求都需要登录访问
                    .anyRequest()
                    .authenticated()
    
                    // 登出行为由自己实现,参考 AuthController#logout
                    .and().logout().disable()
                    // Session 管理
                    .sessionManagement()
                    // 因为使用了JWT,所以这里不管理Session
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    
                    // 异常处理
                    .and().exceptionHandling().accessDeniedHandler(accessDeniedHandler);
    
            // 添加自定义 JWT 过滤器
            http.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        }
    }
    

    相关文章

      网友评论

          本文标题:SpringBoot整合SpringSecurity实现Toke

          本文链接:https://www.haomeiwen.com/subject/najmcrtx.html