您当前的位置: 首页 >  spring

梁云亮

暂无认证

  • 3浏览

    0关注

    1211博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

SpringBoot+SpringSecurity系列 :JWT 工具类

梁云亮 发布时间:2022-01-11 12:10:05 ,浏览量:3

第一步:创建SpringBoot项目
  • 添加依赖

    org.projectlombok
    lombok
    1.18.22


    io.jsonwebtoken
    jjwt-api
    0.11.2


    io.jsonwebtoken
    jjwt-impl
    0.11.2
    runtime


    io.jsonwebtoken
    jjwt-jackson
    0.11.2
    runtime


    org.springframework.boot
    spring-boot-starter-security

  • 修改配置文件application.yml
jwt:
  # 为JWT基础信息加密和解密的密钥,长度需要大于等于43
  # 在实际生产中通常不直接写在配置文件里面。而是通过应用的启动参数传递,并且需要定期修改
  secret: oQZSeguYloAPAmKwvKqqnifiQatxMEPNOvtwPsCLasd
  # JWT令牌的有效时间,单位秒,默认2周
  expiration: 1209600
  header: Authorization
  prefix: hc
第二步:创建JWT工具类
@Slf4j
@Component
//@ConfigurationProperties(prefix = "jwt")
public class JwtUtil {
    /**
     * 携带JWT令牌的HTTP的Header的名称,在实际生产中可读性越差越安全
     */
    @Getter
    @Value("${jwt.header}")
    private String header;

    /**
     * 为JWT基础信息加密和解密的密钥
     * 在实际生产中通常不直接写在配置文件里面。而是通过应用的启动参数传递,并且需要定期修改。
     */
    @Value("${jwt.secret}")
    private String secret;

    /**
     * JWT令牌的有效时间,单位秒
     * - 默认2周
     */
    @Value("${jwt.expiration}")
    private Long expiration;

    /**
     * SecretKey 根据 SECRET 的编码方式解码后得到:
     * Base64 编码:SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));
     * Base64URL 编码:SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(secretString));
     * 未编码:SecretKey key = Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8));
     */
    private static SecretKey getSecretKey(String secret) {
        byte[] encodeKey = Decoders.BASE64.decode(secret);
        return Keys.hmacShaKeyFor(encodeKey);
    }

    /**
     * 用claims生成token
     *
     * @param claims 数据声明,用来创建payload的私有声明
     * @return token 令牌
     */
    private String generateToken(Map claims) {
        SecretKey key = getSecretKey(secret);
        //SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); //两种方式等价

        // 添加payload声明
        JwtBuilder jwtBuilder = Jwts.builder()
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
                .setId(UUID.randomUUID().toString())
                // iat: jwt的签发时间
                .setIssuedAt(new Date())

                // 你也可以改用你喜欢的算法,支持的算法详见:https://github.com/jwtk/jjwt#features
                // SignatureAlgorithm.HS256:指定签名的时候使用的签名算法,也就是header那部分
                .signWith(key, SignatureAlgorithm.HS256)
                .setExpiration(new Date(System.currentTimeMillis() + this.expiration * 1000));

        String token = jwtBuilder.compact();
        return token;
    }

    /**
     * 生成Token令牌
     *
     * @param userDetails 用户
     * @param id          用户编号
     * @return 令牌Token
     */
    public String generateToken(UserDetails userDetails, String id) {
        Map claims = new HashMap();
        claims.put("userId", id);
        claims.put("sub", userDetails.getUsername());
        claims.put("created", new Date());
        return generateToken(claims);
    }

    /**
     * 从token中获取数据声明claim
     *
     * @param token 令牌token
     * @return 数据声明claim
     */
    public Claims getClaimsFromToken(String token) {
        try {
            SecretKey key = getSecretKey(secret);
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(key)
                    .parseClaimsJws(token)
                    .getBody();
            return claims;
        } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | IllegalArgumentException e) {
            log.error("token解析错误", e);
            throw new IllegalArgumentException("Token invalided.");
        }
    }

    public String getUserId(String token) {
        return (String) getClaimsFromToken(token).get("userId");
    }

    /**
     * 从token中获取登录用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getSubjectFromToken(String token) {
        String subject;
        try {
            Claims claims = getClaimsFromToken(token);
            subject = claims.getSubject();
        } catch (Exception e) {
            subject = null;
        }
        return subject;
    }


    /**
     * 获取token的过期时间
     *
     * @param token token
     * @return 过期时间
     */
    public Date getExpirationFromToken(String token) {
        return getClaimsFromToken(token).getExpiration();
    }

    /**
     * 判断token是否过期
     *
     * @param token 令牌
     * @return 是否过期:已过期返回true,未过期返回false
     */
    public Boolean isTokenExpired(String token) {
        Date expiration = getExpirationFromToken(token);
        return expiration.before(new Date());
    }

    /**
     * 验证令牌:判断token是否非法
     *
     * @param token       令牌
     * @param userDetails 用户
     * @return 如果token未过期且合法,返回true,否则返回false
     */
    public Boolean validateToken(String token, UserDetails userDetails) {
        //如果已经过期返回false
        if (isTokenExpired(token)) {
            return false;
        }
        String usernameFromToken = getSubjectFromToken(token);
        String username = userDetails.getUsername();
        return username.equals(usernameFromToken);
    }

}

第三步:SpringSecurity配置文件注入PasswordEncoder
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
第四步:工具类测试
@SpringBootTest
public class JwtUtilTest {

    @Resource
    private JwtUtil jwtUtil;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Test
    void fun() {
        System.out.println(passwordEncoder);
        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        System.out.println(secretKey);
    }

    //生成token
    @Test
    void generateToken() {
        //用户信息
        String encode = passwordEncoder.encode("1234");
        User user = new User("hc", encode, AuthorityUtils.createAuthorityList());
        String token = jwtUtil.generateToken(user, "202");
        System.out.println(token);
    }

    @Test
    void getClaimsFromToken() {
        //用户信息
        String encode = passwordEncoder.encode("1234");
        User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());

        String token = jwtUtil.generateToken(user, "202");
        System.out.println(token);

        Claims claims = jwtUtil.getClaimsFromToken(token);
        System.out.println(claims);
    }

    @Test
    void getUserId() {
        String userId = jwtUtil.getUserId("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ6aGFuZ3NhbiIsImNyZWF0ZWQiOjE2NDE5Nzc3NjA5MTIsInVzZXJJZCI6IjIwMiIsImp0aSI6IjhkMzA0YmYwLTkwZmQtNGFlZC1iMWRkLWEwODE0M2RjMDEzYiIsImlhdCI6MTY0MTk3Nzc2MCwiZXhwIjoxNjQzMTg3MzYwfQ.LJCI5gxEdZYlqfnuS5V5SxwZv-ul411LjGZvbYWQseI");
        System.out.println(userId);
    }

    @Test
    void getSubjectFromToken() {
        //用户信息
        String encode = passwordEncoder.encode("1234");
        User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());
        String token = jwtUtil.generateToken(user, "202");
        System.out.println(token);

        String username = jwtUtil.getSubjectFromToken(token);
        System.out.println(username);
    }

    @Test
    void getExpirationFromToken() {
        //用户信息
        String encode = passwordEncoder.encode("1234");
        User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());
        String token = jwtUtil.generateToken(user, "202");

        System.out.println(token);
        Date date = jwtUtil.getExpirationFromToken(token);
        System.out.println(new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(date));
    }

    @Test
    void isTokenExpired() {
        //用户信息
        String encode = passwordEncoder.encode("1234");
        User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());
        String token = jwtUtil.generateToken(user, "202");

        System.out.println(token);
        Boolean res = jwtUtil.isTokenExpired(token);
        System.out.println(res);
    }

    @Test
    void validateToken() {
        //用户信息
        String encode = passwordEncoder.encode("1234");
        User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());
        String token = jwtUtil.generateToken(user, "202");
        System.out.println(token);

        User user2 = new User("zhangsan", "", AuthorityUtils.createAuthorityList());
        Boolean res = jwtUtil.validateToken(token, user2);
        System.out.println(res);
    }

    //模拟篡改
    @Test
    void fake() {
        // 将我改成你生成的token的第一段(以.为边界)
        String encodedHeader = "eyJhbGciOiJIUzI1NiJ9";
        // 测试4: 解密Header
        byte[] header = Base64.decodeBase64(encodedHeader.getBytes());
        System.out.println(new String(header));

        // 将我改成你生成的token的第二段(以.为边界)
        String encodedPayload = "eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk1NDEsImV4cCI6MTU2Njc5OTE0MX0";
        // 测试5: 解密Payload
        byte[] payload = Base64.decodeBase64(encodedPayload.getBytes());
        System.out.println(new String(payload));

        //用户信息
        String encode = passwordEncoder.encode("1234");
        User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());
        // 测试6: 这是一个被篡改的token,因此会报异常,说明JWT是安全的
        jwtUtil.validateToken("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk3MzIsImV4cCI6MTU2Njc5OTMzMn0.nDv25ex7XuTlmXgNzGX46LqMZItVFyNHQpmL9UQf-aUx", user);
    }

}

关注
打赏
1665409997
查看更多评论
立即登录/注册

微信扫码登录

0.0486s