• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

SpringSecurity-9. 使用SpringSecurity实现JWT

互联网 diligentman 4天前 9次浏览

使用SpringSecurity实现JWT

JWT 令牌的使用方式

SpringSecurity-9. 使用SpringSecurity实现JWT

JWT 认证流程

SpringSecurity-9. 使用SpringSecurity实现JWT

JWT 鉴权流程

SpringSecurity-9. 使用SpringSecurity实现JWT

编码实现

1.重构项目

2.引入jjwt 

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

3.创建工具类

com.zhl.jwtserver.config.auth.jwt.JwtTokenUtil

@Component
@ConfigurationProperties(prefix = "jwt")
@Data
public class JwtTokenUtil {


    private String secret;
    private Long expiration;
    private String header;

    public String generateToken(UserDetails userDetails){
        Map<String,Object> claims=new HashMap<>(2);
        claims.put("sub",userDetails.getUsername());
        claims.put("created",new Date());
        return  generateToken(claims);
    }
    private String generateToken(Map<String,Object> claims){
        Date expirationDate=new Date(System.currentTimeMillis()+expiration);
        return Jwts.builder().setClaims(claims)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512,secret)
                .compact();
    }

    /*从令牌中获取用户名*/
    public String getUsernameFromToken(String token){
        String username;
        try {
            Claims claims=getClaimsFromToken(token);
            username=claims.getSubject();
        }catch (Exception e){
            username=null;
        }
        return username;
    }
    /*从令牌中获取数据声明,*/
    private Claims getClaimsFromToken(String token){
        Claims claims;
        try{
            claims=Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        }catch (Exception e){
             claims=null;
        }
        return  claims;
    }
    /*判断令牌是否过期*/
    public Boolean isTokenExpired(String token){
        try{
            Claims claims=getClaimsFromToken(token);
            Date expiration=claims.getExpiration();
            return expiration.before(new Date());
        }catch (Exception e){
            return false;
        }
    }
    /*
    * 刷新令牌
    * */
    public String refreshToken(String token){
        String refreshedToken;
        try {
            Claims claims=getClaimsFromToken(token);
            claims.put("created",new Date());
            refreshedToken =generateToken(claims);
        }catch (Exception e){
            refreshedToken=null;
        }
        return refreshedToken;
    }
    /**
     * 验证令牌
     * */
    public Boolean validateToken(String token,UserDetails userDetails){
        String username=getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()))&&!isTokenExpired(token);
    }

}

4 引入 AuthenticationManager Bean

com.zhl.jwtserver.config.SecurityConfig#authenticationManager

    @Bean(name= BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
        AuthenticationManager authenticationManager = super.authenticationManager();
        return authenticationManager;
    }

5. JwtAuthService处理登录和刷新令牌

@Service
public class JwtAuthService {

    @Resource
    AuthenticationManager authenticationManager;

    @Resource
    UserDetailsService userDetailsService;

    @Resource
    JwtTokenUtil jwtTokenUtil;
    /*
    * 登录认证换取JWT令牌
    * */
    public String login(String username,String password) throws  CustomException{
        try {
            UsernamePasswordAuthenticationToken upToken =
                    new UsernamePasswordAuthenticationToken(username, password);
            Authentication authenticate = authenticationManager.authenticate(upToken);
            SecurityContextHolder.getContext().setAuthentication(authenticate);
        }catch (AuthenticationException e){
            throw new CustomException(
                    CustomExceptionType.USER_INPUT_ERROR,"用户名或者密码错误!");
        }
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        return jwtTokenUtil.generateToken(userDetails);
    }
    /*根据旧token刷新过期时间*/
    public String refreshToken(String oldToken){
        if(!jwtTokenUtil.isTokenExpired(oldToken)){
            return jwtTokenUtil.refreshToken(oldToken);
        }else{
            return "expired";
        }
    }
}

6.JwtAuthController 对外发布 登录 和刷新令牌接口

@RestController
public class JwtAuthController {

    @Resource
    JwtAuthService jwtAuthService;

    @RequestMapping(value = "/authentication")
    public AjaxResponse login(@RequestBody Map<String,String> map){

        String username = map.get("username");
        String password = map.get("password");
        if(StringUtils.isAnyEmpty(username,password)){
            return AjaxResponse.error(
                    new CustomException(
                            CustomExceptionType.USER_INPUT_ERROR,"用户名或者密码不能为空!"
                    )
            );
        }
        try {
            String jwt = jwtAuthService.login(username, password);
            return AjaxResponse.success(jwt);
        }catch (CustomException e){
            return AjaxResponse.error(e);
        }
    }
    @RequestMapping(value = "/refreshtoken")
    public  AjaxResponse refresh(@RequestHeader("${jwt.header}")String token){
        String s = jwtAuthService.refreshToken(token);
        return  AjaxResponse.success(s);
    }
}

7. 配置SecurityConfig开发访问认证和刷新token的 路径

.antMatchers("/login","/authentication","/refreshtoken").permitAll()

8. 创建JWT授权过滤器,用来进行授权

com.zhl.jwtserver.config.auth.jwt.JwtAuthenticationTokenFilter

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Resource
    JwtTokenUtil jwtTokenUtil;
    @Resource
    MyUserDetailsService myUserDetailsService;
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        String jwtToken = request.getHeader(jwtTokenUtil.getHeader());
        if(StringUtils.isNotEmpty(jwtToken)){
            /*从token中读取用户名*/
            String userName=jwtTokenUtil.getUsernameFromToken(jwtToken);
            /*判断用户名不为空 和 当前用户授权信息不存在*/
            if(userName!=null && SecurityContextHolder.getContext().getAuthentication()==null){
                /*读取权限信息*/
                UserDetails userDetails = myUserDetailsService.loadUserByUsername(userName);
                /*判断是否过期 用户名是否一致*/
                if(jwtTokenUtil.validateToken(jwtToken,userDetails)){
                    /*给使用该jwt令牌 的用户进行授权*/
                    /*1 用户信息,2密码相关信息,3 相关资源权限 */
                    UsernamePasswordAuthenticationToken authenticationToken=
                            new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
                    /*向Security中设置资源权限信息*/
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }

        }
        /*继续过滤器链*/
        filterChain.doFilter(request,response);
    }
}

9. 配置SecurityConfig

9.1    注入授权过滤器

将JwtAuthenticationTokenFilter 放到 UsernamePasswordAuthenticationFilter 前面去执行

http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)

9.2 配置session策略为无状态

.and().sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

9.3 完整代码

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private MyUserDetailsService myUserDetailsService;
    /*代表YML中配置的数据源*/
    @Resource
    private DataSource dataSource;
    @Autowired
    private MyLogoutSuccessHandler myLogoutSuccessHandler;

    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    /*configure 重写方法进行安全认证及授权规则配置*/
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                .logout()
                .logoutUrl("/signout")
                .deleteCookies("JSESSIONID")
                .logoutSuccessHandler(myLogoutSuccessHandler)
            .and()
                .rememberMe()//实现记住我自动登录,核心的代码只有这这一行
                .rememberMeParameter("remember-me-new")//登录时读取的参数
                .rememberMeCookieName("remember-me-cookie")//cookie名称
                .tokenValiditySeconds(2 * 24 * 60 * 60)//cookie失效时间
                .tokenRepository(getPersistentTokenRepository())//设置TokenRepository
            .and().csrf().disable()/*关闭跨站攻击防御*/
            .authorizeRequests()
                /*公开 "/login.html","/login" 不需要验证*/
                .antMatchers("/login","/authentication","/refreshtoken").permitAll()
                .antMatchers("/index").authenticated()
                /*对于所有的请求 ,都要使用rbacService中规则验证*/
                .anyRequest().access("@rbacService.hasPermission(request,authentication)")
                /*session的创建策略配置 */
            .and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        /*动态加载用户权限信息*/
        auth.userDetailsService(myUserDetailsService)
                .passwordEncoder(passwordEncoder());//配置BCrypt加密
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /*将项目中静态资源路径开放出来*/
    @Override
    public void configure(WebSecurity web) throws Exception {
        /*这里标记的资源不必进入过滤器校验即可公开访问*/
        web.ignoring().antMatchers(
                "/css/**",
                "/fonts/**",
                "/img/**",
                "/js/**"
        );
    }
    /*
     * 配置TokenRepository组件。
     * */
    @Bean
    public PersistentTokenRepository getPersistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);

        return tokenRepository;
    }
    @Bean(name= BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
        AuthenticationManager authenticationManager = super.authenticationManager();
        return authenticationManager;
    }

}

 

{{o.name}}


{{m.name}}


喜欢 (0)