目录
1、JWT简介
1.1、Session+Cookie认证
1.2、Token认证
1.3、对比
2、JWT构成
头部(Header)
载荷(Payload)
签名(Signature)
3、实行流程
1、JWT简介
JWT是JSON Web Token的缩写,是一个开放标准(RFC 7519),它定义了一种紧凑和自包含的方式,用于作为JSON对象在各方之间安全地传输信息。JWT可以用于身份验证和授权,由于它是数字签名的。
github: https://github.com/jwtk/jjwt
1.1、Session+Cookie认证
http 协议本身是无状态的协议,就意味着当有用户向系统使用账户名称和暗码进行用户认证之后,下一次请求还要再一次用户认证才行。由于我们不能通过 http 协议知道是哪个用户发出的请求,以是假如要知道是哪个用户发出的请求,那就需要在服务器保存一份用户信息 ,这就需session。Session认证是一种基于服务器端的认证方式,它是通过在服务器端保存用户会话信息来实现的。当用户第一次访问服务器时,服务器会天生一个Session ID并将其存储在Cookie中。用户在后续的请求中将会带上这个Cookie,服务器可以通过这个Cookie来辨认用户并获取用户的会话信息。
1.2、Token认证
token认证方式跟 session 的方式流程差不多,不同的地方在于保存的是一个 token 值到 redis,token 一般是一串随机的字符(比如UUID),value 一般是用户ID,而且设置一个逾期时间。每次请求服务的时候带上 token 在请求头,后端吸收到token 则根据 token 查一下 redis 是否存在,假如存在则表现用户已认证,假如 token 不存在则跳到登录界面让用户重新登录,登录成功后返回一个 token 值给客户端。
1.3、对比
- 在传统的用户登录认证中,由于http是无状态的,以是都是接纳session方式。用户登录成功,服务端会保证一个session,当然会给客户端一个sessionId,客户端会把sessionId保存在cookie中,每次请求都会携带这个sessionId。cookie+session这种模式通常是保存在内存中,而且服务从单服务到多服务碰面临的session共享题目,随着用户量的增多,开销就会越大。而JWT不是这样的,只需要服务端天生token,客户端保存这个token,每次请求携带这个token,服务端认证分析就可。
- JWT方式校验方式更加简朴便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录,验证token更为简朴
2、JWT构成
JWT 由三部门构成:头部(Header)、**载荷(Payload)**和 签名(Signature),从官方的图解也可以看到如下:(JWT官网:JSON Web Tokens - jwt.io)
头部(Header)
头部(Header): JWT头部通常由两部门构成,第一部门是声明类型,比方JWT,第二部门是声明所使用的算法,比方HMAC SHA256或者RSA等。 末了使用 Base64 URL 算法将上述 JSON 对象转换为字符串保存 。
- {
- "alg": "HS256", // 签名算法
- "typ": "JWT" // 令牌类型
- }
复制代码 载荷(Payload)
携带一些用户信息 和默认字段; 默认情况下 JWT 是未加密的,任何人都可以解读其内容,因此一些敏感信息不要存放于此,以防信息泄露。 JSON 对象也使用 Base64 URL 算法转换为字符串后保存,可以反编码。
- /**
- ss (issuer):签发人/发行人
- sub (subject):主题
- aud (audience):用户
- exp (expiration time):过期时间
- nbf (Not Before):生效时间,在此之前是无效的
- iat (Issued At):签发时间
- jti (JWT ID):用于标识该 JWT
- **/
- // 自定义
- {
- //默认字段
- "sub":"主题123",
- //自定义字段
- "name":"gcxy",
- "isAdmin":"true",
- "loginTime":"2023-8-22 10:00:03"
- }
复制代码 签名(Signature)
防止Token被窜改、确保安全性 ,签名过程:
1.将头部和载荷按照顺序拼接成一个字符串;
2.使用私钥对拼接后的字符串进行加密,得到一个签名;
3.将签名添加到头部中。
3、实行流程
jwt实行流程大抵如下:
代码实例:
导入依赖:
- <!-- JWT依赖 -->
- <dependency>
- <groupId>com.auth0</groupId>
- <artifactId>java-jwt</artifactId>
- <version>3.4.0</version>
- </dependency>
复制代码 封装JWT工具类
- package com.gcxy.demo3.utils;
- import com.auth0.jwt.JWT;
- import com.auth0.jwt.JWTCreator;
- import com.auth0.jwt.algorithms.Algorithm;
- import com.auth0.jwt.interfaces.Claim;
- import com.auth0.jwt.interfaces.DecodedJWT;
- import org.springframework.stereotype.Component;
- import java.util.Calendar;
- import java.util.Date;
- import java.util.Map;
- @Component
- public class JwtUtil {
- private static String secret = "1234";
- /**
- * 生成token
- * @param subject
- * @return
- */
- public static String createToken(Map<String, String> subject){
- // 过期时间
- Date date = new Date(new Date().getTime() + 3600 * 1000);
- JWTCreator.Builder builder = JWT.create();
- subject.forEach((k, v) -> {
- builder.withClaim(k, v);
- });
- // 设置过期时间
- Calendar calendar = Calendar.getInstance();
- calendar.add(Calendar.DATE, 1); // 设置默认过期时间
- builder.withExpiresAt(calendar.getTime()); // 指定过期时间
- return builder.sign(Algorithm.HMAC256(secret));
- }
- /**
- * 校验token
- * @param token
- * @return
- */
- public static DecodedJWT verifyToken(String token) {
- return JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
- }
- /**
- * 获取token信息
- * @param token
- * @return
- */
- public static Map<String, Claim> getTokenInfo(String token) {
- return JWT.require(Algorithm.HMAC256(secret)).build().verify(token).getClaims();
- }
- }
复制代码 创建拦截 器:
- package com.gcxy.demo3.interceptor;
- import com.auth0.jwt.interfaces.DecodedJWT;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import com.gcxy.demo3.utils.JwtUtil;
- import org.springframework.stereotype.Component;
- import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
- import javax.annotation.Resource;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.util.HashMap;
- @Component
- public class TokenInterceptor extends HandlerInterceptorAdapter {
- @Resource
- private JwtUtil jwtConfig;
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- HashMap<String, String> map = new HashMap<>();
- // Token 验证
- String token = request.getHeader("Authorization");
- try {
- // 校验成功放行请求
- DecodedJWT verifyToken = jwtConfig.verifyToken(token);
- return true;
- } catch (Exception e) {
- map.put("msg", "token验证失败: " + e);
- }
- // 校验失败返回失败信息
- String json = new ObjectMapper().writeValueAsString(map);
- response.setContentType("application/json;charset=UTF-8");
- response.getWriter().write(json);
- return false;
- }
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- }
- @Override
- public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- }
- }
复制代码 注册拦截器:
- package com.gcxy.demo3.config;
- import com.gcxy.demo3.interceptor.TokenInterceptor;
- 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) {
- // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
- registry.addInterceptor(new TokenInterceptor())
- .addPathPatterns("/**")
- .excludePathPatterns("/login")
- ;
- }
- }
复制代码 创建测试controller
- package com.gcxy.demo3.controller;
- import com.auth0.jwt.interfaces.DecodedJWT;
- import com.gcxy.demo3.utils.JwtUtil;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PostMapping;
- import org.springframework.web.bind.annotation.RestController;
- import javax.servlet.http.HttpServletRequest;
- import java.util.HashMap;
- import java.util.Map;
- @RestController
- public class UserController {
- @PostMapping("/login")
- public Map<String, Object> login(String username, String password) {
- Map<String, Object> map = new HashMap<>();
- try{
- // 生成token
- Map<String, String> payload = new HashMap<>();
- payload.put("username", "zhangsan");
- payload.put("password", "123456");
- String token = JwtUtil.createToken(payload);
- map.put("code", 200);
- map.put("msg", "登录成功");
- map.put("token", token);
- }catch (Exception e){
- System.out.println(e.getMessage());
- map.put("code", 500);
- map.put("msg", "登录失败");
- }
- return map;
- }
- @GetMapping("/getUserInfo")
- public Map<String, Object> getUserInfo(HttpServletRequest request) {
- Map<String, Object> map = new HashMap<>();
- String token = request.getHeader("Authorization");
- DecodedJWT tokenInfo = JwtUtil.verifyToken(token);
- String username = tokenInfo.getClaim("username").asString();
- String password = tokenInfo.getClaim("password").asString();
- System.out.println("username = " + username);
- System.out.println("password = " + password);
- map.put("code", 200);
- map.put("msg", "请求成功");
- return map;
- }
- }
复制代码 测试结果:


练习拓展:
结合Redis实现token验证
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |