JWT实现token

JWT是什么?

        JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token,并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输。

JWT的认证流程如下:

1、首先,前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建议的方式是通过SSL加密的传输(HTTPS),从而避免敏感信息被嗅探

2、后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼接后签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串 3、后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退出登录时删除保存的JWT Token即可

4、前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)

5、后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方是否是自己等等

6、验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果

JWT:JSON Web Token,token就是一段字符串,由三部分组成:Header,Payload,Signature

Header

JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示。

{
"alg": "HS256",
"typ": "JWT"
}

        在上面的代码中,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。

Payload        

        有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择。

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

除以上默认字段外,我们还可以自定义私有字段,如下例:

{
"userId": "1",
"userAccount": "wupengyu",
}

Signature

一段签名数据

类似于:

SING="ahfuau&@$&@$@%";

JWT使用步骤

1、导入maven依赖

这里使用的时auth0中的jwt,hutool中也有jwt,此处不再过多研究



    com.auth0
    java-jwt
    3.4.0

2、编写JWTUtils工具包

此处可以作为通用JWT工具类

public class JWTUtils {
    private static final String SING = "!Q@W3e4r%T^Y";
    /**
     *  生成token  header.payload.sing
     * */
    public static String getToken(Map map){
        //获取当前时间
        Calendar instance = Calendar.getInstance();
        //Calendar中add,对天数进行操作,设置当前时间后七天
        instance.add(Calendar.DATE,7);
        //创建JWT builder
        JWTCreator.Builder builder = JWT.create();
        //设置payload
        map.forEach((k,v)->{
                builder.withClaim(k,v);
        });
        String token = builder
                .withExpiresAt(instance.getTime())//指定令牌过期时间
                .sign(Algorithm.HMAC256(SING));
        System.out.println(token);
        return token;
    }
​
    /**
     *  验证token 合法性
     * */
    public static void verify(String token){
        //验签算法,要与签名算法相同
         JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
    }
​
    /**
     *  获取token信息
     * */
    public static DecodedJWT getTokenInfo(String token){
        DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
        //从DecodedJWT中拿token的信息
        return decodedJWT;
    }
}

3、编写token的拦截器

        如果在每个接口处都对token进行校验,会造成代码冗余,所以我们写一个拦截器或者AOP,如果是分布式系统,那就将此操作写进网关。

public class JWTInterceptor implements HandlerInterceptor {
​
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map map = new HashMap<>();
        //获取请求头中的令牌
        String token = request.getHeader("token");
        try{
            JWTUtils.verify(token);//验证令牌
            return true;//请求放行
        }catch (SignatureVerificationException e){
            e.printStackTrace();
            map.put("msg","无效签名");
        }catch (TokenExpiredException e){
            e.printStackTrace();
            map.put("msg","token过期");
        }catch (AlgorithmMismatchException e){
            e.printStackTrace();
            map.put("msg","token算法不一致");
        }catch (Exception e){
            e.printStackTrace();
            map.put("msg","token无效");
        }
        map.put("state",false);
        //将map转化为json
        Gson gson = new Gson();
        String json = gson.toJson(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().print(json);
        return false;
    }
}

4、配置token拦截器

        自主选择不需要拦截的路径进行配置即可

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
​
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/user/**")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/login/byPhone")
                .excludePathPatterns("/user/sendMsg");
​
    }
}

5、之后,在登录接口中,用户登陆成功后,生成一个token,并作为响应值返回给前端,前端将其设置到请求头中。如需使用token中的用户信息,调用token的解析方法就可以。

扩展:如果想注销时删除token,除了在前端删除请求头外,还应设置黑名单,可以使用redis设置黑名单,防止恶意使用还未过期的token进行数据访问。

你可能感兴趣的:(java)