美文网首页
SpringBoot + SpringSecurity + Jw

SpringBoot + SpringSecurity + Jw

作者: 唔哒喂 | 来源:发表于2022-10-23 00:56 被阅读0次

    前后端分离情况。

    最重要的两步就是自定义拦截器和定义SecurityConfig配置类。

    新建SpringBoot项目
    Maven引入依赖:

    <!-- JWT -->
            <dependency>
              <groupId>io.jsonwebtoken</groupId>
              <artifactId>jjwt</artifactId>
              <version>0.9.1</version>
            </dependency>
    
                    <!--    MYSQL        -->
                    <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <!--    MyBatis-Plus     -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.5.2</version>
            </dependency>
    
            <!--   FastJson      -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.83</version>
            </dependency>
    
            <!--    Reids        -->
    <!--        <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency> -->
    

    SecurityCofig

    1、SecurityConfig中定义了哪些接口可以被开放
    最少需要开放登录接口、注销接口、【注册】
    简单设置如下:

    A1、关闭csrf、关闭session
    A2、开放白名单
    A3、将自定义注解放到默认的UsernamePasswordAuthenticationFilter前。
    @Resource 注入的是:自定义过滤器、登录失败处理器、权限异常处理器
    除@Configuration另外两个注解主要是开启Security注解鉴权、启用Web安全

    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Resource
        JwtFilter jwtFilter;
    
        @Resource
        NotLoginHandler notLoginHandler;
    
        @Resource
        AuthorizationErrorHandler authorizationErrorHandler;
    
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    
            //1、关闭csrf,关闭Session
            http
                    .csrf().disable()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    
            //2、设置不需要认证的URL
            http
                    .authorizeRequests()
                    //允许未登录的用户进行访问
                    .antMatchers("/user/**").anonymous()
                    //其余url都要认证才能访问
                    .anyRequest().authenticated();
    
            http.exceptionHandling() //异常处理
                    .authenticationEntryPoint(notLoginHandler)
                    .accessDeniedHandler(authorizationErrorHandler);
    
    
            http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
        }
    

    自定义Jwt过滤器

    在我的理解中Jwt存储了主要有三个部分:过期时间、用户非重要信息、用户权限信息[可]
    【或者用token+redis的方式】
    在由前端请求时Header中携带这个token。

    主要步骤为:对请求进行拦截 &【解析Jwt、生成Security令牌】、放行

    这里和不分离项目不同的是,是继承这个OncePreRequestFIlter。
    而不是Security内置的AbstractAuthenticationProcessingFilter

    
    @Component
    public class JwtFilter extends OncePerRequestFilter {
    
        @Resource
        JwtUtils jwtUtils;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
    
            // 1、获取前端Header-Token
            String header = request.getHeader(SecurityConstant.TOKEN_NAME);
            if (header != null && StringUtils.isNoneBlank(header)){
                // 2、解析token
    
                Claims claimByToken = jwtUtils.getClaimByToken(header);
    
                if (!jwtUtils.isTokenExpired(claimByToken)) {
                    String id = claimByToken.getId();
                    String subject = claimByToken.getSubject();
                    String rolesJson = (String) claimByToken.get(SecurityConstant.JWT_AUTHORITIES);
                    Set<String> roles = JSONObject.parseObject(rolesJson, Set.class);
                    Set<GrantedAuthority> authorities = new HashSet<>();
                    for (String role :
                            roles) {
                        authorities.add(new SimpleGrantedAuthority(role));
                    }
                    // 3、在SecurityHolder设置Authentication
                    SecurityContextHolder.getContext().setAuthentication(new JwtAuthenticationToken(id, subject, authorities));
                }
            }
            chain.doFilter(request, response);
        }
    }
    

    ELSE

    到这里我认为SpringSecurity的基本骨架已经搭建好了。
    SpringSecurity还需要添加的代码:
    1、Security的AuthenticationToken——让Security认可你登录的令牌
    2、登录失败【未登录】处理器
    3、权限校验异常处理器

    不重要:自定义登录接口、JwtUtils,一些小方法代码
    见文末其他代码。

    其他代码

    Security的AuthenticationToken

    /**
    * jwt身份验证令牌
    *
    */
    
    public class JwtAuthenticationToken extends AbstractAuthenticationToken {
    
        private String username;
        private String userId;
    
        public JwtAuthenticationToken(String userId, String username, Collection<? extends GrantedAuthority> authorities ){
            super(authorities);
            this.userId = userId;
            this.username = username;
            super.setAuthenticated(true);
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getUserId() {
            return userId;
        }
    
        public void setUserId(String userId) {
            this.userId = userId;
        }
    
        @Override
        public Object getCredentials() {
            return null;
        }
    
        @Override
        public Object getPrincipal() {
            return this.username;
        }
    }
    

    登录失败[未登录]处理器

    /**
     * 未登录处理程序
     */
    @Component
    public class NotLoginHandler implements AuthenticationEntryPoint {
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(HttpStatus.FORBIDDEN.value());
            ResBean notLogin = ResBean.NOT_LOGIN;
            JsonResult<Object> result = new JsonResult<>();
            result.setMessage(notLogin.getMessage());
            result.setSuccess(false);
            result.setTotal(0);
            result.setStatus(notLogin.getCode());
            result.setBody(notLogin);
            response.getWriter().write(JSONObject.toJSONString(result));
        }
    }
    

    权限校验异常处理器

    @Component
    public class AuthorizationErrorHandler implements AccessDeniedHandler {
    
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(HttpStatus.FORBIDDEN.value());
            ResBean notLogin = ResBean.NO_ROLES;
            JsonResult<Object> result = new JsonResult<>();
            result.setMessage(notLogin.getMessage());
            result.setSuccess(false);
            result.setTotal(0);
            result.setStatus(notLogin.getCode());
            result.setBody(notLogin);
            response.getWriter().write(JSONObject.toJSONString(result));
        }
    }
    

    JwtUtils

    @Data
        @Component
        public class JwtUtils {
    
            private long expire;
            private String secret;
            private String header;
    
    
            /**
            * 生成令牌
            *
            * @param user  用户
            * @param roles 角色
            * @return {@link String}
            */
            public String generateToken(SysUser user, Set<String> roles) {
    
                Date nowDate = new Date();
                Date expireDate = new Date(nowDate.getTime() + 1000 * SecurityConstant.EXPIRE_TIME);
    
                return Jwts.builder()
                    .setHeaderParam("typ", "JWT")
                    .setId(String.valueOf(user.getId()))
                    .setSubject(user.getNickname())
                    .claim(SecurityConstant.JWT_AUTHORITIES, JSONObject.toJSONString(roles))
                    .setIssuedAt(nowDate)
                    .setExpiration(expireDate)
                    .signWith(SignatureAlgorithm.HS512, SecurityConstant.JWT_SIGN)
                    .compact();
            }
    
            /**
            * 解析
            *
            * @param jwt jwt
            * @return {@link Claims}
            */
            public Claims getClaimByToken(String jwt) {
                try {
                    return Jwts.parser()
                        .setSigningKey(SecurityConstant.JWT_SIGN)
                        .parseClaimsJws(jwt)
                        .getBody();
                } catch (Exception e) {
                    return null;
                }
            }
    
            /**
    * 是令牌过期
    *
    * @param claims 
    * @return boolean
    */
            public boolean isTokenExpired(Claims claims) {
                return claims.getExpiration().before(new Date());
            }
    
        }
    

    登录部分截图

    image.png

    RespBean为公共对象返回枚举

    不展示了。

    相关文章

      网友评论

          本文标题:SpringBoot + SpringSecurity + Jw

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