2.jwt

JWT详解
1. 介绍
JWT简称 JSON Web Token,也就是通过**JSON形式作为Web应用中的令牌**,用于各方之间安全地将信息作为JSON对象传输,在数据传输的过程中还可以完成数据加密、签名等相关处理。
2. 流程图
 
2.1 认证流程
首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。
后端核对用户名和密码成功后,将用户的id等其他信息作为 JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT(Token)。形成的JWT就是一个形同lll.mmm.sss的字符串。 token = head.payload.singurater
后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。
前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)
后端检查是否存在,如存在验证JWT的有效性
2.2 jwt优势
简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快
 
自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
 
因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。
 
不需要在服务端保存会话信息,特别适用于分布式微服务。
 
3. JWT
3.1 header(表头)
标头通常由两部分组成:令牌的类型(即JWT)和所使用的签名算法,例如HMAC SHA256(默认)或RSA。它会使用Base64编码组成JWT结构的第一部分。
注意:Base64是一种编码,也就是说,它是可以被翻译回原来的样子的,它并不是一种加密过程。
3.2 Payload(载荷)
将能用到的用户信息放在 Payload中。官方建议不要放特别敏感的信息,例如密码。
3.3 Signature(签名信息)
签证由三部分组成,header和payload分别经base64Url(一种在base64上做了一点改变的编码)编码后由’.’连接,服务器生成秘钥(secret),连接之后的字符串在经header中声明的加密方式和秘钥加密,再用’.'和加密前的字符串连接。服务器验证token时只会验证第三部分。
 
header (base64Url) . payload (base64Url) . secret(header (base64Url)+payload (base64Url))
 
4. Session方式
session方法存储会有很大的缺点。
 
@RestController
@RequestMapping("test")
public class SessionController {
    @PostMapping("login")
    public String login(String userName, String password, HttpServletRequest request){
        // 将用户名和密码拼接后设置在session作用域中
        request.getSession().setAttribute("user", userName + password);
        return "Login Success!";
    }
}
5. Jwt实现
5.1 依赖
<!-- jwt的依赖包 -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.4.0</version>
</dependency>
 
5.2 工具类
 
package com.liu.jwt.util;
 
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
 
import java.util.Calendar;
import java.util.Map;
 
/*
 * jwt工具类
 */
public class JwtUtils {
 
    // sign
    private static final String SIGN = "token@lms";
 
 
    // 生成token,header.payload.sign
    public static String getToken(Map<String, String> map){
 
        // 创建jwt builder对象
        JWTCreator.Builder builder = JWT.create();
 
        // 添加payload
        map.forEach((k, v) -> {
            builder.withClaim(k, v);
        });
 
        // 设置过期时间
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, 7);
        // 设置过期时间和添加签名信息
        String token = builder.withExpiresAt(calendar.getTime())
                .sign(Algorithm.HMAC256(SIGN));
        return token;
    }
 
    // 验证token的合法性
    public static void verify(String token){
        // 如果验证失败会直接抛出异常信息
        JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
    }
 
    // 获取token中的信息(也可以和上面部分的代码直接合并)
    public static DecodedJWT getTokenInfo(String token){
        DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
        return verify;
    }
}
 
5.3 拦截器
package com.liu.jwt.interceptor;
 
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.liu.jwt.util.JwtUtils;
import org.springframework.web.servlet.HandlerInterceptor;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
 
/**
 * jwt拦截器,执行token的验证工作(集的配置拦截器)
 */
public class JwtInterceptor implements HandlerInterceptor {
 
    /**
     * 在目标方法执行之前执行
     * @param request
     * @param response
     * @param handler 目标方法
     * @return true,代表放行, false,表示禁止放行
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HashMap<String, Object> map = new HashMap<>();
        // 因为token存放在请求头中,
        String token = request.getHeader("token");
 
        // 验证token
        try {
            // 直接验证token是否正确,如果token不正确,会直接抛出异常信息被全局异常捕获
            JwtUtils.verify(token);
            // 验证通过,直接放行请求
            return true;
 
        } catch (SignatureVerificationException e){
            map.put("msg", "无效签名");
        } catch (TokenExpiredException e){
            map.put("msg", "token已过期");
        } catch (AlgorithmMismatchException e){
            map.put("msg", "token算法不一致");
        } catch (Exception e) {
            map.put("msg", "token无效");
        }
        map.put("code", "500");
        // 将map转为json数据信息
        // 因为response底层也封装了json,也可以使用fastJson
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().println(json);
        // 禁止放行请求
        return false;
    }
}
配置拦截器
package com.liu.jwt.config;
 
import com.liu.jwt.interceptor.JwtInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
 
    // 配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JwtInterceptor())
                // 拦截所有的请求信息
                .addPathPatterns("/**")
                // 只放行/login登录请求,
                .excludePathPatterns("/login");
    }
}
 
5.4 Controller
package com.liu.jwt.controller;
 
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.liu.jwt.entity.User;
import com.liu.jwt.service.UserService;
import com.liu.jwt.util.JwtUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
 
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
 
 
@RestController
public class UserController {
 
    @Resource
    private UserService userService;
 
    /**
     * 用户登录
     * @param user
     * @return
     */
    @PostMapping("login")
    public Map<String, Object> login(User user){
        HashMap<String, Object> map = new HashMap<>();
        System.out.println("user.getUsername() = " + user.getUsername());
        System.out.println("user.getPassword() = " + user.getPassword());
 
        try {
            User userLogin = userService.login(user);
            // 用户存放负载信息,从而调用jwt生成相应的token信息
            HashMap<String, String> payload = new HashMap<>();
            payload.put("id", userLogin.getId());
            payload.put("username", userLogin.getUsername());
 
            // 生成token信息
            String token = JwtUtils.getToken(payload);
 
            map.put("code", "200");
            map.put("msg","登录成功");
            map.put("token", token);
        } catch (Exception e) {
            map.put("code", "500");
            map.put("msg", e.getMessage());
        }
        return map;
    }
 
 
    // 方式1:未使用拦截器,代码冗余
    // 登录之后执行相应的业务逻辑
 // @PostMapping("test")
    public Map<String, Object> verify(String token){
        HashMap<String, Object> map = new HashMap<>();
        System.out.println("token = " + token);
 
        // 验证token
        try {
            JwtUtils.verify(token);
            // 获取token的返回值信息,类型为DecodedJWT
            DecodedJWT tokenInfo = JwtUtils.getTokenInfo(token);
            map.put("code", "200");
            map.put("msg","请求成功");
            return map;
        } catch (SignatureVerificationException e){
            map.put("msg", "无效签名");
        } catch (TokenExpiredException e){
            map.put("msg", "token已过期");
        } catch (AlgorithmMismatchException e){
            map.put("msg", "token算法不一致");
        } catch (Exception e) {
            map.put("msg", "token无效");
        }
        map.put("code", "500");
        return map;
    }
 
 
    // 方式2:配置了拦截器(只有登录之后才能进行使用该方法)
    @PostMapping("test")
    public Map<String, Object> test(HttpServletRequest request){
        HashMap<String, Object> map = new HashMap<>();
        // 从请求头中获取token信息
        String token = request.getHeader("token");
        DecodedJWT tokenInfo = JwtUtils.getTokenInfo(token);
 
        // 处理自己的业务逻辑
        map.put("code", "200");
        map.put("msg","请求成功");
 
        String username = tokenInfo.getClaim("username").asString();
        String id = tokenInfo.getClaim("id").asString();
        map.put("username", username);
        map.put("id", id);
 
        return map;
    }
}
 
posted @ 2022-10-03 17:25  midiyu  阅读(34)  评论(0编辑  收藏  举报