当前位置 主页 > 服务器问题 > win服务器问题汇总 >

    Spring Security代码实现JWT接口权限授予与校验功能

    栏目:win服务器问题汇总 时间:2019-12-04 14:18

    通过笔者前两篇文章的说明,相信大家已经知道JWT是什么,怎么用,该如何结合Spring Security使用。那么本节就用代码来具体的实现一下JWT登录认证及鉴权的流程。

    一、环境准备工作

    建立Spring Boot项目并集成了Spring Security,项目可以正常启动 通过controller写一个HTTP的GET方法服务接口,比如:“/hello” 实现最基本的动态数据验证及权限分配,即实现UserDetailsService接口和UserDetails接口。这两个接口都是向Spring Security提供用户、角色、权限等校验信息的接口 如果你学习过Spring Security的formLogin登录模式,请将HttpSecurity配置中的formLogin()配置段全部去掉。因为JWT完全使用JSON接口,没有from表单提交。 HttpSecurity配置中一定要加上csrf().disable(),即暂时关掉跨站攻击CSRF的防御。这样是不安全的,我们后续章节再做处理。

    以上的内容,我们在之前的文章中都已经讲过。如果仍然不熟悉,可以翻看本号之前的文章。

    ## 二、开发JWT工具类

    通过maven坐标引入JWT工具包jjwt

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

    在application.yml中加入如下自定义一些关于JWT的配置

    jwt: 
     header: JWTHeaderName
     secret: aabbccdd 
     expiration: 3600000 
    其中header是携带JWT令牌的HTTP的Header的名称。虽然我这里叫做JWTHeaderName,但是在实际生产中可读性越差越安全。 secret是用来为JWT基础信息加密和解密的密钥。虽然我在这里在配置文件写死了,但是在实际生产中通常不直接写在配置文件里面。而是通过应用的启动参数传递,并且需要定期修改。 expiration是JWT令牌的有效时间。

    写一个Spring Boot配置自动加载的工具类。

    @Data
    @ConfigurationProperties(prefix = "jwt") //配置自动加载,prefix是配置的前缀
    @Component
    public class JwtTokenUtil implements Serializable {
     private String secret;
     private Long expiration;
     private String header;
     /**
     * 生成token令牌
     *
     * @param userDetails 用户
     * @return 令token牌
     */
     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);
     }
     /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
     public String getUsernameFromToken(String token) {
     String username;
     try {
      Claims claims = getClaimsFromToken(token);
      username = claims.getSubject();
     } catch (Exception e) {
      username = null;
     }
     return username;
     }
     /**
     * 判断令牌是否过期
     *
     * @param token 令牌
     * @return 是否过期
     */
     public Boolean isTokenExpired(String token) {
     try {
      Claims claims = getClaimsFromToken(token);
      Date expiration = claims.getExpiration();
      return expiration.before(new Date());
     } catch (Exception e) {
      return false;
     }
     }
     /**
     * 刷新令牌
     *
     * @param token 原令牌
     * @return 新令牌
     */
     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;
     }
     /**
     * 验证令牌
     *
     * @param token 令牌
     * @param userDetails 用户
     * @return 是否有效
     */
     public Boolean validateToken(String token, UserDetails userDetails) {
     SysUser user = (SysUser) userDetails;
     String username = getUsernameFromToken(token);
     return (username.equals(user.getUsername()) && !isTokenExpired(token));
     }
     /**
     * 从claims生成令牌,如果看不懂就看谁调用它
     *
     * @param claims 数据声明
     * @return 令牌
     */
     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();
     }
     /**
     * 从令牌中获取数据声明,如果看不懂就看谁调用它
     *
     * @param token 令牌
     * @return 数据声明
     */
     private Claims getClaimsFromToken(String token) {
     Claims claims;
     try {
      claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
     } catch (Exception e) {
      claims = null;
     }
     return claims;
     }
    }