基于JWT的token认证机制

1. 一个JWT实际上就是一个字符串,由三部分组成 头部,载荷,签名
头部:事描述类型,签名,算法等 可以被表示成一个JSON对象
载荷:存放有效信息的地方 包含三个部分
(1)标准注册中的声明-建议但不强制使用
  iss:jwt签发者
  sub:jwt所面向的用户
  aud:接收jwt的一方
  exp:jwt的过期时间,时间必须大于签发时间
  nbf:定义在什么时间以前,这个jwt都是不可用的
  iat:jwt的签发时间
  jti:jwt的唯一身份标识,用来作为token
(2)公共的声明
  一般是公司相关的信息,由于这部分可以被解密,所以不要添加敏感信息
(3)私有的声明
  可以被解密,,
  可以自定义claim
  载荷的例子 {"sub":"1233","name":"John","admin":true}
签证:三部分
  header(base64后的)
  payload(base64后的)
  secret
2. 名词解释 Base64
  64个可打印二进制数据 2^6为64 每6个比特是一个单元,打印一个字符,3个字节有24个比特
  对应4个Base64 JDK中提供了BASE64Encoder BASE64Decoder 编码,解码
3. java的jjwt
  JJWT是一个提供端到端的JWT创建和验证的Java库
4. 使用

   (一)引入依赖

  
    io.jsonwebtoken
    jjwt
    0.6.0
  

  (二)测试使用

 
package BigTest.jwt;


import com.JwtBootApplication;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.text.SimpleDateFormat;
import java.util.Date;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JwtBootApplication.class)
public class CreateJwtTest {
    /*
    * java.lang.IllegalArgumentException:
    *     secret key byte array cannot be null or empty.
    * 是因为secret太简单了 没有加时间
    * */
    @Test
    public void testJwt(){
        JwtBuilder jwtBuilder = Jwts.builder().setId("888")
                .setSubject("小白")
                .setIssuedAt(new Date()) //setIssuedAt用于设置签发时间
                .signWith(SignatureAlgorithm.HS256, "onetwothree"); //signWith用于设置签名秘钥
        System.out.println(jwtBuilder.compact());
        //eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzMyOTAwOTh9.-lqlLqmAG2qQ15Ge7IlcgIAgj0V54L0GCA5dJfM6Lw4

    }
    @Test
    public void ParseJwtTest(){
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzMyOTAwOTh9.-lqlLqmAG2qQ15Ge7IlcgIAgj0V54L0GCA5dJfM6Lw4";
        Claims claims = Jwts.parser().setSigningKey("onetwothree").parseClaimsJws(token).getBody();

        System.out.println("id="+claims.getId());
        System.out.println("subject="+claims.getSubject());
        System.out.println("IssueAt="+claims.getIssuedAt());
    }

    /**
     * 带过期时间的jwt-token
     * ExpiredJwtException: token过期报错
     */
    @Test
    public void testJwt_time(){
        long now = System.currentTimeMillis();
        long exp = now + 1000*60;//设置时间为1分钟
        JwtBuilder jwtBuilder = Jwts.builder().setId("888")
                .setSubject("小白")
                .setIssuedAt(new Date()) //setIssuedAt用于设置签发时间
                .setExpiration(new Date(exp))//setExpiration设置token过期时间
                .signWith(SignatureAlgorithm.HS256, "onetwothree"); //signWith用于设置签名秘钥
        System.out.println(jwtBuilder.compact());

    }
    @Test
    public void ParseJwtTest_time(){
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzMyOTg0MDUsImV4cCI6MTU3MzI5ODQ2NX0.Rx-0BrJ2032aUz_vrttXw-9_idfQe3LHzCnLOWpsaqs";
        Claims claims = Jwts.parser().setSigningKey("onetwothree").parseClaimsJws(token).getBody();
        System.out.println("id="+claims.getId());
        System.out.println("subject="+claims.getSubject());
        System.out.println("IssueAt="+claims.getIssuedAt());
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss");
        System.out.println("签发时间:"+sdf.format(claims.getIssuedAt()));
        System.out.println("过期时间:"+sdf.format(claims.getExpiration()));
        System.out.println("当前时间:"+sdf.format(new Date()) );
    }
    /*
    * 自定义claims,上面的只存储了id和subject 自定义可以存储自己向存储的内容
    * */
    @Test
    public void testJwt_claims(){
        long now = System.currentTimeMillis();
        long exp = now + 1000*60; //设置过期时间为1分钟
        JwtBuilder claim = Jwts.builder().setId("9768")
                .setSubject("zhangsan")
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256,"onetwothree")
                .setExpiration(new Date(exp))
                .claim("roles", "admin")
                .claim("address", "beijing");
        System.out.println(claim.compact());
    }
    @Test
    public void ParseJwtTest_claim(){
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI5NzY4Iiwic3ViIjoiemhhbmdzYW4iLCJpYXQiOjE1NzMzMDg0ODUsImV4cCI6MTU3MzMwODU0NSwicm9sZXMiOiJhZG1pbiIsImFkZHJlc3MiOiJiZWlqaW5nIn0.Y-8hZIAlWzegSadQWSQGRc9gUBGlBznRT984jtqQZ-s";
        Claims claims = Jwts.parser().setSigningKey("onetwothree").parseClaimsJws(token).getBody();

        System.out.println("id:"+claims.getId());
        System.out.println("subject:"+claims.getSubject());
        System.out.println("roles:"+claims.get("roles"));
        System.out.println("logo:"+claims.get("address"));
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss");
        System.out.println("签发时间:"+sdf.format(claims.getIssuedAt()));
        System.out.println("过期时间:"+sdf.format(claims.getExpiration()));
        System.out.println("当前时间:"+sdf.format(new Date()) );

    }

}
View Code

  (三)在springboot中的使用

   在application.yml添加配置

jwt:
  config:
    key: zhedoumeishenme
    ttl: 360000

 编写utils

package com.xxy.server.common.utils;

import io.jsonwebtoken.*;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.Date;

@ConfigurationProperties("jwt.config")//拿到 .yml中的参数
public class JwtUtils {
    private String key;
    private long ttl; //一个小时

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public long getTtl() {
        return ttl;
    }

    public void setTtl(long ttl) {
        this.ttl = ttl;
    }

    /**
     * 生成jwt
     * @param id
     * @param subject
     * @param roles
     * @return
     */
    public String createJWT(String id, String subject, String roles){
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        JwtBuilder builder = Jwts.builder().setId(id)
                .setSubject(subject)
                .signWith(SignatureAlgorithm.HS256, key)
                .setIssuedAt(now)
                .claim("roles", roles);
        if(ttl > 0){
            builder.setExpiration(new Date(nowMillis+ttl));
        }

        return builder.compact();
    }

    /**
     * 解析JWT
     * @param jwtStr
     * @return
     */
    public Claims parseJWT(String jwtStr){
        return Jwts.parser()
                .setSigningKey(key)
                .parseClaimsJws(jwtStr)
                .getBody();
    }
}
Jwt请求拦截器
package com.xxy.server.common.interceptors;

import com.xxy.server.common.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Description: Jwt请求拦截器
 * @Author: xuxiaoyu
 * @Create: 2019-11-10 20:45
 */
@Component
public class JwtFilter extends HandlerInterceptorAdapter {
    @Autowired
    private JwtUtils jwtUtils;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("经过了jwtfilter拦截器");
        final String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            Claims claims = jwtUtils.parseJWT(authHeader);
            if(claims != null){
                if("admin".equals(claims.get("roles"))){
                    request.setAttribute("admin_claims", claims);
                }
                if("user".equals(claims.get("roles"))){
                    request.setAttribute("user_claims", claims);
                }
            }
        }
        return true;
    }
}
View Code

   配置到mvc中

package com.xxy.server.common.config;

import com.xxy.server.common.interceptors.JwtFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
 * description:该类可以来扩展Spirng MVC的功能
 */
@Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
    @Autowired
    private JwtFilter jwtFilter;
    /**
     * description:添加默认视图映射,当访问localhost:8080/时,跳转到index.html页面
     * params :  registry
     * @return void
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");

    }
    /**
     * description: Jwt请求拦截器,任何请求都会经过这个拦截器
     * @param registry
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtFilter)
                .addPathPatterns("/**")
                .excludePathPatterns("/login","/captcha");
    }
}
View Code

  然后在方法中就可以判断使用了

 Claims claims = (Claims) request.getAttribute("admin_claims");
        if (claims == null) {
            return HttpResultUtil.error("权限不足");
        }

 

 

 

你可能感兴趣的:(基于JWT的token认证机制)