Token_JWT

在这里插入图片描述

Token

概述

Token 是服务端生成的一串字符串,以作为客户端进行请求的一个令牌,当第一次登录后,服务器生成一个 Token 便将此 Token 返回给客户端,以后客户端只需带上这个 Token 前来请求数据即可,无需再次带上用户名和密码。

优势

  • 无状态、可扩展

    • 在客户端存储的Tokens是无状态的,并且能够被扩展。基于这种无状态和不存储Session信息,负载负载均衡器能够将用户信息从一个服务传到其他服务器上。tokens自己hold住了用户的验证信息。
  • Tokens能够创建与其它程序共享权限的程序。

  • 跨域

  • 安全

    • 请求中发送token而不再是发送cookie能够防止CSRF(跨站请求伪造)。即使在客户端使用cookie存储tokencookie也仅仅是一个存储机制而不是用于认证。不将信息存储在Session中,让我们少了对session操作。
  • Token 设置过期时间

JWT

概述

官网:https://jwt.io/

GitHub:https://github.com/auth0/java-jwt

API:https://javadoc.io/doc/com.auth0/java-jwt/latest/index.html

JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地将信息作为 JSON 对象传输。 由于此信息是经过数字签名的,因此可以被验证和信任。 可以使用秘钥(使用 HMAC 算法)或使用 RSA 或 ECDSA 的公用/专用密钥对对 JWT 进行签名。

在前后端或者服务器进行交互的过程中,通过 JSON 形式作为 Web 应用中的令牌,以完成数据传输、加密、签名等相关处理操作。

JWT 认证流程

  1. 首先,前端通过 Web 表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个 HTTP POST 请求。建议的方式是通过 SSL 加密的传输(https协议),从而避免敏感信息被嗅探。
  2. 后端核对用户名和密码成功后,将用户的 id 等其他信息作为 JWT Payload (载荷),将其与头部分别进行 Base64 编码拼接后再生成一个签名,形成一个 JWT(Token)。形成的 JWT 就是一个形同 111.ZzZz.xxx 的字符串。
  3. 后端将 JWT 字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在 localStorage 或 sessionStorage 上,退出登录时前端删除保存的 JWT 即可。
  4. 前端在每次请求时将 JWT 放入 HTTP Header 中的 Authorization 位。(解决XSS和XSRF问题)
  5. 后端检查是否存在,如存在验证 JWT 的有效性。例如,检查签名是否正确;检查 Token 是否过期;检查 Token 的接收方是否是自己(可选)。
  6. 验证通过后后端使用 JWT 中包含的用户信息进行其他逻辑操作,返回相应结果。

JWT 的优势

  • 简洁(Compact):可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快
  • 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
  • 因为Token是以 JSON 加密的形式保存在客户端的,所以 JWT 是跨语言的,原则上任何web形式都支持。
  • 不需要在服务端保存会话信息,特别适用于分布式微服务。

JWT 的组成【重点】

JWT 有3个组成部分,每个部分之间用 . 进行分隔

  • Header
  • Payload
  • Signature

格式为

xxxxx.yyyyy.zzzzz

1、头部(header) 声明类型以及加密算法,例如 HMAC、SHA256或 RSA。

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

2、载荷(payload) 携带一些用户身份信息,用户id,颁发机构,颁发时间,过期时间等。用Base64进行了处理。这一段其实是明文,所以一定不要放敏感信息。例如

{"id": 200, "username": "易烊千玺"}

3、签证(signature) 签名信息,使用了自定义的盐然后加密后的结果,目的就是为了保证签名的信息没有被别人改过,这个一般是让服务器验证的。

JWT 入门

public class JwtTest {
    // 通过JWT获取token
    @Test
    public void createToken() {
        HashMap<String, Object> header = new HashMap<>();
        header.put("alg", "HS256");
        header.put("typ", "JWT");

        Calendar expireTime = Calendar.getInstance();

        expireTime.add(Calendar.MINUTE, 30);

        // 创建一个Token
        String token = JWT.create().
                        // 头部
                        withHeader(header).
                        // 载荷
                        withClaim("username", "易烊千玺").
                        withClaim("id", 1).
                        withExpiresAt(expireTime.getTime()).
                        // 签名(盐)
                        sign(Algorithm.HMAC256("ABC8D!EFG"));

        System.out.println(token);
    }

    // 验证JWT的token
    @Test
    public void verifyToken() {
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("ABC8D!EFG")).build();

        DecodedJWT verify = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjE4MTM4NzY0LCJ1c2VybmFtZSI6IuaYk-eDiuWNg-eOuiJ9.IhMvu1CPUVd94rPTu6HNQTGP_fBTOaJaAofD7WTDFwo");

        System.out.println("token头:" + verify.getHeader());
        System.out.println("token载荷:" + verify.getPayload());
        System.out.println("token签名:" + verify.getSignature());
        System.out.println("id:" + verify.getClaim("id").asInt());
        System.out.println("username:" + verify.getClaim("username").asString());
        System.out.println("过期时间:" + verify.getExpiresAt());

    }
}

JWT 工具类

/**
 * JWT工具类
 */
public class JwtUtils {
    // token头算法
    private static final String ALG = "HS256";
    // token头类型
    private static final String TYP = "JWT";
    // token签名算法
    private static final Algorithm ALGORITHM;
    // token头
    private static final HashMap<String, Object> HEADER = new HashMap<>();
    // token签名
    private static final String SIGN = "QwErTyUi";

    // 静态代码块加载token头以及token签名算法
    static {
        HEADER.put("alg", ALG);
        HEADER.put("typ", TYP);
        ALGORITHM = Algorithm.HMAC256(SIGN);
    }

    /**
     * 获取Token
     *
     * @param claim 载荷
     * @param expireTime 过期时间,单位:分钟
     * @return token
     */
    public static String getToken(Map<String, Object> claim, Integer expireTime) {
        // 返回创建的token
        return JWT.create().
                // 设置头
                withHeader(HEADER).
                // 设置过期时间,单位为分钟
                withExpiresAt(new Date(System.currentTimeMillis() + expireTime * 1000 * 60)).
                // 设置载荷
                withClaim("claim", claim).
                // 设置签名
                sign(ALGORITHM);
    }

    /**
     * 解析token
     *
     * @param token token
     * @return 验证的信息
     */
    public static DecodedJWT parseToken(String token) {
        // 获取token
        return JWT.require(ALGORITHM).build().verify(token);
    }

    /**
     * 根据token获取载荷信息
     *
     * @param token token
     * @return 验证的载荷
     */
    public static Map<String, Object> getClaim(String token) {
        // 获取token中的载荷信息
        return parseToken(token).getClaim("claim").asMap();
    }

    /**
     * 判断是否过期
     *
     * @param token token
     * @return 是否已过期 
     */
    public static boolean isExpiration(String token) {
        return parseToken(token).getExpiresAt().before(new Date());
    }
}

测试

public class JwtUtilsTest {
    @Test
    public void test() {
        // 准备载荷
        HashMap<String, Object> map = new HashMap<>();
        map.put("username", "易烊千玺");

        // 获取token
        String token = JwtUtils.getToken(map, 30);

        System.out.println(token);

        // 获取token中的载荷
        Map<String, Object> claim = JwtUtils.getClaim(token);
        String username = (String) claim.get("username");

        System.out.println(username);
    }
}

SpringMvc 整合 JWT

pom.xml

<dependencies>
    <!--spring Mvc-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.5</version>
    </dependency>

    <!--Servlet-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
    </dependency>

    <!--JWT-->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.14.0</version>
    </dependency>

    <!--Json解析-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.12.2</version>
    </dependency>

    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.16</version>
    </dependency>
</dependencies>

实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String username;
    private String password;
}

业务层及实现类

public interface UserService {
    User login(String username, String password);
}

@Service
public class UserServiceImpl implements UserService {
    @Override
    public User login(String username, String password) {
        if (username.equals("易烊千玺") && password.equals("123456")) {
            return new User(username, password);
        }

        return null;
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置前端控制器-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--读取springmvc配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMvc.xml</param-value>
        </init-param>
        <!--启动时加载-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--配置前端控制器的访问路径-->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录页</title>
</head>
<body>
    <form>
        <table align="center">
            <tr>
                <td>账号</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>密码</td>
                <td><input type="password" name="password"></td>
            </tr>

            <tr>
                <td colspan="1" align="center">
                    <input type="button" value="登录" οnclick="login()">
                </td>
            </tr>
        </table>
    </form>
</body>
<script src="js/jquery-1.8.3.min.js"></script>
<script type="text/javascript">

    function login() {
        // 序列化表单内容
        var data = $("form").serialize();

        // 访问后端资源
        $.ajax({
            type: "post",
            url: "/user/login",
            data: data,
            success: function (resp) {
                // 将token存入localStorage
                localStorage.setItem("token", resp.token);

                // 跳转页面
                $(location).attr('href', '/index.jsp');
            }
        })
    }
</script>
</html>
常规用法

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>主页</title>
</head>
<body>
    <h1 align="center" style="color: blue">欢迎<span id="username"></span>来到主页</h1>
</body>
<script src="js/jquery-1.8.3.min.js"></script>
<script type="text/javascript">
    $(function () {
        // 从localStorage中获取token
        var token = localStorage.getItem("token");

        // 如果token为空
        if (token === null) {
            // 跳转回登录页面
            $(location).attr('href', '/login.jsp');
        }

        // 访问后端接口获取资源
        $.ajax({
            type: "post",
            url: "/user/get",
            data: {"token": token},
            success: function (resp) {

                // 如果响应状态为200
                if (resp.code === 200) {
                    // 获取资源
                    $("#username").text(resp.data.username)
                }
            }
        })
    })
</script>
</html>

controller

@Controller
@RequestMapping("user")
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping("login")
    @ResponseBody
    public Map<String, Object> login(User user) {
        // 准备一个Map用来返回Json类型数据
        Map<String, Object> map = new HashMap<>();
        map.put("code", -1);
        map.put("message", "登录失败");

        // 登录并获取User对象
        User result = userService.login(user.getUsername(), user.getPassword());

        // 如果User对象不为空说明登录成功
        if (result != null) {
            // 准备载荷信息
            Map<String, Object> claim = new HashMap<>();

            claim.put("username", result.getUsername());

            // 获取token
            String token = JwtUtils.getToken(claim, 30);

            map.put("code", 200);
            map.put("message", "登录成功");
            map.put("token", token);
        }

        return map;
    }

    @PostMapping("get")
    @ResponseBody
    public Map<String, Object> get(String token) {
        // 准备一个Map用来返回Json类型数据
        Map<String, Object> map = new HashMap<>();
        map.put("code", -1);

        // 如果token不存在,访问被拒绝
        if (token == null) {
            map.put("message", "token不存在,访问被拒绝");
        }

        // 通过工具类获取载荷信息
        try {
            Map<String, Object> claim = JwtUtils.getClaim(token);

            map.put("message", "允许访问");
            map.put("code", 200);
            map.put("data", claim);

            // 抛出对应的异常
        } catch (AlgorithmMismatchException e) {
            map.put("message", "算法不匹配");
        } catch (InvalidClaimException e) {
            map.put("message", "非法载荷");
        } catch (SignatureVerificationException e) {
            map.put("message", "签名不匹配");
        } catch (TokenExpiredException e) {
            map.put("message", "token已过期");
        } catch (Exception e) {
            map.put("message", "token异常,访问被终止");
        }

        return map;
    }
}

springMvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.fc"/>

    <!-- 视图解析器对象 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>

    <!-- 开启SpringMVC框架注解的支持 -->
    <mvc:annotation-driven/>
    
    <!--放行静态资源-->
    <mvc:default-servlet-handler/>
</beans>
使用拦截器

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>主页</title>
</head>
<body>
    <h1 align="center" style="color: blue">欢迎<span id="username"></span>来到主页</h1>
</body>
<script src="js/jquery-1.8.3.min.js"></script>
<script type="text/javascript">
    $(function () {
        // 从localStorage中获取token
        var token = localStorage.getItem("token");

        // 如果token为空
        if (token === null) {
            // 跳转回登录页面
            $(location).attr('href', '/login.jsp');
        }

        // 访问后端接口获取资源
        $.ajax({
            type: "get",
            url: "/user/get",
            async: false,
            headers: {"token": token},
            success: function (resp) {

                // 如果响应状态为200
                if (resp.code === 200) {
                    // 获取资源
                    $("#username").text(resp.data.username)
                }
            }
        })
    })
</script>
</html>

controller

@Controller
@RequestMapping("user")
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping("login")
    @ResponseBody
    public Map<String, Object> login(User user) {
        // 准备一个Map用来返回Json类型数据
        Map<String, Object> map = new HashMap<>();
        map.put("code", -1);
        map.put("message", "登录失败");

        // 登录并获取User对象
        User result = userService.login(user.getUsername(), user.getPassword());

        // 如果User对象不为空说明登录成功
        if (result != null) {
            // 准备载荷信息
            Map<String, Object> claim = new HashMap<>();

            claim.put("username", result.getUsername());

            // 获取token
            String token = JwtUtils.getToken(claim, 30);

            map.put("code", 200);
            map.put("message", "登录成功");
            map.put("token", token);
        }

        return map;
    }

    @GetMapping("get")
    @ResponseBody
    public Map<String, Object> get(HttpServletRequest request) {
        // 准备一个Map用来返回Json类型数据
        Map<String, Object> map = new HashMap<>();
        map.put("code", 200);
        map.put("message", "请求成功");
        map.put("data", request.getAttribute("claim"));

        return map;
    }
}

自定义 Jwt 拦截器

/**
 * 自定义Jwt拦截器
 */
public class JwtInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从请求头中获取token
        String token = request.getHeader("token");

        Map<String, Object> map = new HashMap<>();
        map.put("code", -1);
        
        try {
            // 解析token并获取载荷
            Map<String, Object> claim = JwtUtils.getClaim(token);

            // 设置到域对象中
            request.setAttribute("claim", claim);

            return true;

            // 抛出对应的异常
        } catch (AlgorithmMismatchException e) {
            map.put("message", "算法不匹配");
        } catch (InvalidClaimException e) {
            map.put("message", "非法载荷");
        } catch (SignatureVerificationException e) {
            map.put("message", "签名不匹配");
        } catch (TokenExpiredException e) {
            map.put("message", "token已过期");
        } catch (Exception e) {
            map.put("message", "token异常,访问被终止");
        }

        // 获取Jackson核心处理对象
        ObjectMapper objectMapper = new ObjectMapper();

        // 对象转Json字符串
        String json = objectMapper.writeValueAsString(map);

        // 设置响应内容类型
        response.setContentType("application/json; charset=UTF-8");

        // 发送响应
        response.getWriter().println(json);

        return false;
    }
}

springMvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.fc"/>

    <!-- 视图解析器对象 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>

    <!-- 开启SpringMVC框架注解的支持 -->
    <mvc:annotation-driven/>

    <!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--拦截的路径-->
            <mvc:mapping path="/**"/>
            <!--不拦截的路径-->
            <mvc:exclude-mapping path="/user/login"/>
            <!--自定义拦截器-->
            <bean class="com.fc.interceptor.JwtInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    
    <!--放行静态资源-->
    <mvc:default-servlet-handler/>
</beans>

【扩展】自定义 JWT 并加入权限认证

1、pom.xml引入依赖

<!--json解析-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.2</version>
</dependency>

<!--Apache下的包含各种格式的简单编码器和解码器,如Base64和十六进制-->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.15</version>
</dependency>

2、jwt工具类

/**
 * 自定义jwt工具类
 */
public class JwtUtils {
    /**
     * 获取JWT
     *
     * @param claim 载荷
     * @param salt  盐
     * @return JWT
     */
    public static String getJWT(Map<String, Object> claim, String salt) {
        // 添加一个时间戳
        claim.put("time", System.currentTimeMillis() + 1000 * 60 * 30);

        // 未编码的头部
        String decodedHeader = "{\"alg\":\"HS256\"}";

        // 使用base64算法对二进制数据进行编码,获取编码后的头部
        String header = Base64.getEncoder().encodeToString(decodedHeader.getBytes(StandardCharsets.UTF_8));

        // 准备json解析的对象
        ObjectMapper objectMapper = new ObjectMapper();

        String jsonDecoded = null;

        try {
            // 将载荷转为json字符串
            jsonDecoded = objectMapper.writeValueAsString(claim);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        // 使用base64算法对二进制数据进行编码,获取编码后的载荷
        String payload = Base64.getEncoder().encodeToString(jsonDecoded.getBytes(StandardCharsets.UTF_8));

        // 获取16进制的md5加密后的签名
        String signature = DigestUtils.md5Hex(jsonDecoded + salt);

        // 准备一个StringBuilder
        StringBuilder stringBuilder = new StringBuilder();

        // 将头部,载荷,签名进行拼接
        StringBuilder append = stringBuilder.append(header).append(".").append(payload).append(".").append(signature);

        // 类型转换获取JWT
        return new String(append);
    }

    /**
     * 解析JWT
     *
     * @param jwt  jwt
     * @param salt 盐
     * @return 响应结果
     */
    public static Map<String, Object> decodeJwt(String jwt, String salt) {
        // 声明响应的结果,默认为false
        Map<String, Object> result = new HashMap<>();
        result.put("success", false);

        // 拆分jwt为字符串数组
        String[] jwtArr = jwt.split("\\.");

        // 获取未解码的载荷
        String payloadBase64 = jwtArr[1];

        // 获取解码后的载荷字节数组
        byte[] decode = Base64.getDecoder().decode(payloadBase64.getBytes(StandardCharsets.UTF_8));

        // 获取解码后的载荷
        String payload = new String(decode);

        // 准备json解析对象
        ObjectMapper objectMapper = new ObjectMapper();

        Map<String, Object> claim = null;

        try {
            // 将json类型的载荷解析为Map对象
            claim = objectMapper.readValue(payload, new TypeReference<Map<String, Object>>() {});

            // 获取载荷中的时间
            Long time = (Long) claim.get("time");

            // 如果大于当前时间说明jwt已失效,携带错误信息并返回
            if (time < System.currentTimeMillis()) {
                result.put("message", "当前jwt已失效");
                return result;
            }
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        // 载荷加上盐值
        payload += salt;

        // 使用md5生成签名
        String sign = DigestUtils.md5Hex(payload);

        // 和jwt中的签名对比
        if (sign.equals(jwtArr[2])) {
            // 将载荷放入响应内容中并返回
            result.put("success", true);
            result.put("message", "访问成功");
            result.putAll(claim);
            return result;
        }

        // 签名不相同说明jwt被篡改了,返回错误信息
        result.put("message", "非法的jwt");
        return result;
    }
}

3、测试类

public class JwtTest {
    @Test
    public void test() {
        // 准备一个盐值
        String salt = "123456";

        // 准备载荷
        Map<String, Object> map = new HashMap<>();
        map.put("username", "送你一朵小红花");
        map.put("id", 1);
        map.put("age", 21);

        // 使用工具类生成jwt
        String jwt = JwtUtils.getJWT(map, salt);

        System.out.println(jwt);

        // 使用盐值解析jwt
        Map<String, Object> payload = JwtUtils.decodeJwt(jwt, salt);

        // 判断是否解析成功
        boolean success = (boolean) payload.get("success");

        if (success) {
            System.out.println(payload.get("username"));
        } else {
            System.out.println(payload.get("message"));
        }
    }
}

4、前端页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<script src="js/jquery-1.8.3.min.js"></script>
<body>
    <form action="user/login" method="post">
        <table align="center">
            <caption><h1>测试jwt</h1></caption>
            <tr>
                <td>账号</td>
                <td><input type="text" name="username"></td>
            </tr>

            <tr>
                <td>密码</td>
                <td><input type="password" name="password"></td>
            </tr>

            <tr>
                <td align="center" colspan="2">
                    <input type="reset" value="重置">
                    <button type="button" onclick="login()">提交</button>
                </td>
            </tr>
        </table>
    </form>
</body>
<script type="text/javascript">
    function login() {
        var data = $("form").serialize();

        $.ajax({
            url: "user/login",
            data: data,
            success: function (res) {
                localStorage.setItem("jwt", res.jwt);
                window.location.href = "/success.html";
            }
        })
    }
</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录成功页</title>
</head>
<script src="js/jquery-1.8.3.min.js"></script>
<body>
    <button type="button" onclick="add()">添加</button>
    <button type="button" onclick="show()">查看</button>
</body>
<script type="text/javascript">
    var jwt = localStorage.getItem("jwt");

    function add() {
        $.ajax({
            url: "user/add",
            data: {"jwt": jwt},
            success: function (res) {
                alert(res.message);
            }
        })
    }

    function show() {
        $.ajax({
            url: "user/select",
            data: {"jwt": jwt},
            success: function (res) {
                alert(res.message);
            }
        })
    }
</script>
</html>

5、控制层

@RestController
@RequestMapping("user")
public class UserController {

    @RequestMapping("login")
    public Map<String, Object> login(String username, String password, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>();

        // 判断登录用户是否为管理员用户,如果是,添加管理员权限
        if (username.equals("易烊千玺") && password.equals("123456")) {
            map.put("auth", "admin");
        } else {
            // 不是管理员,添加普通权限
            map.put("auth", "user");
        }
        
        // 将生成的jwt存入响应内容中
        map.put("jwt", JwtUtils.getJWT(map, "salt"));

        return map;
    }

    @RequestMapping("add")
    public Map<String, Object> add(@RequestParam String jwt) {
        // 解码jwt获取载荷
        Map<String, Object> map = JwtUtils.decodeJwt(jwt, "salt");

        // 获取载荷中的参数
        boolean success = (boolean) map.get("success");

        // 判断jwt是否解码成功
        if (success) {
            // 获取jwt中存储的用户权限
            String auth = String.valueOf(map.get("auth"));

            // 判断是否为管理员权限
            if (auth.equals("admin")) {
                map.put("message", "欢迎管理员进行访问");
            } else {
                map.put("message", "权限不够");
            }
        }

        return map;
    }

    @RequestMapping("select")
    public Map<String, Object> select(@RequestParam String jwt) {
        // 解码jwt获取载荷
        Map<String, Object> map = JwtUtils.decodeJwt(jwt, "salt");

        // 获取载荷中的参数
        boolean success = (boolean) map.get("success");

        // 判断jwt是否解码成功
        if (success) {
            String auth = String.valueOf(map.get("auth"));

            // 判断是否为管理员权限
            if (auth.equals("admin")) {
                map.put("message", "欢迎管理员访问");
            } else {
                map.put("message", "欢迎访问");
            }
        }

        return map;
    }
}
    boolean success = (boolean) map.get("success");

    // 判断jwt是否解码成功
    if (success) {
        // 获取jwt中存储的用户权限
        String auth = String.valueOf(map.get("auth"));

        // 判断是否为管理员权限
        if (auth.equals("admin")) {
            map.put("message", "欢迎管理员进行访问");
        } else {
            map.put("message", "权限不够");
        }
    }

    return map;
}

@RequestMapping("select")
public Map<String, Object> select(@RequestParam String jwt) {
    // 解码jwt获取载荷
    Map<String, Object> map = JwtUtils.decodeJwt(jwt, "salt");

    // 获取载荷中的参数
    boolean success = (boolean) map.get("success");

    // 判断jwt是否解码成功
    if (success) {
        String auth = String.valueOf(map.get("auth"));

        // 判断是否为管理员权限
        if (auth.equals("admin")) {
            map.put("message", "欢迎管理员访问");
        } else {
            map.put("message", "欢迎访问");
        }
    }

    return map;
}

}


好的,以下是一个使用 flask_jwt_extended 和 token_in_blocklist_loader 的例子: ```python from flask import Flask from flask_jwt_extended import JWTManager, jwt_required, create_access_token, get_jwt_identity, get_raw_jwt app = Flask(__name__) app.config['JWT_SECRET_KEY'] = 'super-secret' # 设置 JWT 密钥 jwt = JWTManager(app) # 模拟一个 token 黑名单 blocklist = set() # 定义 token_in_blocklist_loader 回调函数 @jwt.token_in_blocklist_loader def check_if_token_in_blocklist(jwt_header, jwt_payload): jti = jwt_payload['jti'] return jti in blocklist # 定义登录接口 @app.route('/login', methods=['POST']) def login(): username = request.json.get('username', None) password = request.json.get('password', None) if username != 'admin' or password != 'admin': return jsonify({"msg": "Bad username or password"}), 401 access_token = create_access_token(identity=username) return jsonify(access_token=access_token), 200 # 定义需要 token 认证的接口 @app.route('/protected', methods=['GET']) @jwt_required def protected(): current_user = get_jwt_identity() return jsonify(logged_in_as=current_user), 200 # 定义注销接口 @app.route('/logout', methods=['DELETE']) @jwt_required def logout(): jti = get_raw_jwt()['jti'] blocklist.add(jti) return jsonify({"msg": "Successfully logged out"}), 200 if __name__ == '__main__': app.run() ``` 这个例子中,我们定义了一个 token 黑名单 `blocklist`,并在 `token_in_blocklist_loader` 回调函数中检查 token 是否在黑名单中。当用户注销时,我们将该 token 的 `jti` 添加到黑名单中。这样,即使该 token 没有过期,也无法再次使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值