• 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;
        }
    }
     
  • 相关阅读:
    【图文并茂,点赞收藏哦!】重学巩固你的Vuejs知识体系
    RabbitMQ概念及控制台介绍
    RabbitMQ入门介绍及环境搭建
    C# 利用PdfSharp生成Pdf文件
    利用pdf.js在线展示PDF文档
    Python办公自动化之Excel转Word
    C#利用ServiceStack.Redis访问Redis
    Redis基础之事务
    Redis基础之配置文件
    第0天 | 12天搞定Pyhon,前言
  • 原文地址:https://www.cnblogs.com/midiyu/p/16750831.html
Copyright © 2020-2023  润新知