spring boot如何基于JWT实现单点登录详解

这里是关于如何基于JWT实现Spring Boot单点登录的攻略:

什么是JWT

JWT(JSON Web Token),是一种用于身份验证的标准。它由三部分组成:Header(头部)、Payload(负载)和Signature(签名)。

Header部分一般用于描述Token的类型和 signature使用的算法,例如:

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

Payload部分是JWT的主要内容,例如用户的身份信息、访问权限等,例如:

{
  "userId": "123",
  "name": "Tom",
  "roles": ["admin", "user"]
}

Signature部分是将header和payload加密生成的签名,用于验证JWT的有效性。

JWT使用起来非常方便,可以通过验证签名的方式来确认身份是否合法,不需要再通过服务端的session来存储登录信息。

基于JWT实现Spring Boot单点登录步骤

下面我们就来介绍如何基于JWT实现Spring Boot单点登录。

第一步:添加依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

这里我们使用jjwt作为JWT的依赖库。

第二步:编写TokenUtils类

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class TokenUtils {

    private static final String SECRET = "your_secret_key"; // 生成JWT时使用的密钥
    private static final int EXPIRATION_TIME = 864000000; // JWT过期时间,单位毫秒,这里设置为10天

    public static String createToken(String id, String subject, Map<String, Object> claims) {
        SecretKey key = Keys.hmacShaKeyFor(SECRET.getBytes());
        return Jwts.builder()
                .setId(id)
                .setSubject(subject)
                .addClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(key, SignatureAlgorithm.HS256)
                .compact();
    }

    public static Claims parseToken(String token) {
        SecretKey key = Keys.hmacShaKeyFor(SECRET.getBytes());
        return Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

}

这里我们定义了两个方法:createToken用于生成JWT Token,parseToken用于解析JWT Token获取其中的信息。其中需要设置:

  • SECRET:生成Token时使用的密钥
  • EXPIRATION_TIME:Token的有效期

第三步:编写登录接口

@RestController
@RequestMapping("/auth")
public class LoginController {

    @PostMapping("/login")
    public Map<String, Object> login(@RequestBody User user) {
        // 校验用户名和密码,如果校验通过,生成JWT Token
        if (checkUser(user)) {
            Map<String, Object> claims = new HashMap<>();
            claims.put("roles", user.getRoles());
            String token = TokenUtils.createToken(user.getId(), user.getUsername(), claims);
            Map<String, Object> result = new HashMap<>();
            result.put("token", token);
            return result;
        } else {
            throw new RuntimeException("登录失败");
        }
    }

    private boolean checkUser(User user) {
        // 校验用户信息,返回true或false
    }

}

在这个接口中,我们首先根据用户提交的用户名和密码进行校验。如果校验通过,我们就可以利用TokenUtils类生成Token并返回给前端。

第四步:编写安全拦截器

@Component
public class AuthInterceptor implements HandlerInterceptor {

    private static final String HEADER_NAME = "Authorization"; // 前端请求头中存储Token的字段名

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader(HEADER_NAME);
        if (StringUtils.isEmpty(token)) {
            throw new RuntimeException("未授权");
        }
        try {
            Claims claims = TokenUtils.parseToken(token);
            request.setAttribute("claims", claims);
            return true;
        } catch (Exception e) {
            throw new RuntimeException("Token无效");
        }
    }

}

在这个安全拦截器中,我们首先从前端提交的请求头中读取Token,并使用TokenUtils类解析其中的内容。如果Token无效,则抛出异常,否则就将解析后的信息存储在request中,以便后续的处理。

示例一:访问需要Token验证的接口

现在我们就可以编写需要Token验证的接口了。例如:

@RestController
@RequestMapping("/api")
public class ApiController {

    @GetMapping("/user/{id}")
    public User getUser(@PathVariable String id, HttpServletRequest request) {
        Claims claims = (Claims) request.getAttribute("claims");
        User user = userService.getUserById(id);
        if (!user.getUsername().equals(claims.getSubject())) {
            throw new RuntimeException("无权限访问");
        }
        return user;
    }

}

在这个接口中,我们通过@PathVariable注解获取用户id,并使用request.getAttribute("claims")获取从Token中解析出的信息。如果当前用户与Token中存储的用户相同,就返回该用户的信息,否则就抛出“无权限访问”的异常。

示例二:单点登录

接下来我们可以在多个应用之间实现单点登录。例如,我们现在有两个应用:

  • app1:http://localhost:8081
  • app2:http://localhost:8082

我们可以在app1中编写如下代码:

@RestController
@RequestMapping("/sso")
public class SsoController {

    @GetMapping("/login")
    public String login(String username, String password) {
        // 校验用户名和密码
        if (checkUser(username, password)) {
            Map<String, Object> claims = new HashMap<>();
            claims.put("roles", "user");
            String token = TokenUtils.createToken(UUID.randomUUID().toString(), username, claims);
            // 登录成功,重定向到app2,并传递Token
            return "redirect:http://localhost:8082/api/cas?ticket=" + token;
        } else {
            return "登录失败";
        }
    }

    private boolean checkUser(String username, String password) {
        // 校验用户信息,返回true或false
    }

}

在这个控制器中,我们首先校验用户名和密码。如果校验通过,我们就生成Token,并将其作为参数传递给app2的接口。app2的接口可以像这样来实现:

@RestController
@RequestMapping("/api")
public class ApiController {

    @GetMapping("/cas")
    public String cas(String ticket) {
        // 使用Token验证用户身份
        Claims claims = TokenUtils.parseToken(ticket);
        if (!"user".equals(claims.get("roles"))) {
            throw new RuntimeException("无权限访问");
        }
        return "登录成功";
    }

}

现在,我们在浏览器中访问http://localhost:8081/sso/login,输入正确的用户名和密码后,就会自动跳转到http://localhost:8082/api/cas页面,并返回“登录成功”的信息。这就实现了单点登录。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:spring boot如何基于JWT实现单点登录详解 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • 基于module-info.class的问题

    “基于module-info.class的问题” 在Java 9之前是不存在的。 Java SE 9中引入了模块化系统,它引入了一个新的文件模块描述符module-info.java。module-info.java包含有关模块的信息,包括模块依赖关系,公共包导入等。在模块化系统中,其他类需要使用Java模块,需要module-info.java中导入的包。…

    Java 2023年5月19日
    00
  • JavaScript实现九九乘法表的简单实例

    以下是“JavaScript实现九九乘法表的简单实例”的完整攻略。 展示九九乘法表 首先,在HTML文件中创建一个表格,用于展示九九乘法表: <table> <thead> <tr> <th>&times;</th> <th>1</th> <th>2&lt…

    Java 2023年6月15日
    00
  • Java Character类的详解

    Java Character类的详解 1. Character类的概述 在Java中,Charater类是用来对单个字符进行操作的类。 Charater类用于记录来自Unicode字符集的单个字符,由16位的无符号整数表示。 2. Character类的常用方法 2.1. 获取unicode值 public static int getNumericValu…

    Java 2023年5月29日
    00
  • Java中的IllegalStateException是什么?

    Java中的IllegalStateException 在Java编程中,当我们的应用程序处于不适合执行给定操作的状态时,会抛出IllegalStateException。 通俗一点讲,即在方法调用之前或之后进行检查,如果当前对象状态无法支持这种方法调用,则抛出IllegalStateException异常。 何时会抛出IllegalStateExcepti…

    Java 2023年4月27日
    00
  • 使用jackson实现对象json之间的相互转换(spring boot)

    下面是使用Jackson库实现对象和JSON格式的相互转换的完整攻略。 前置条件 本文需要你已经掌握Spring Boot框架的基础知识,并且对于Java对象与JSON的基础知识有所了解。 介绍 Jackson是一个Java库,用于将Java对象序列化为JSON格式的字符串,并将JSON格式的字符串反序列化为Java对象。Jackson支持在Java对象和J…

    Java 2023年5月26日
    00
  • JAVA随机打乱数组顺序的方法

    下面是“JAVA随机打乱数组顺序的方法”的完整攻略: 题目分析 首先,我们需要了解一下题目的意思,了解题目的要求是什么。题目要求我们实现一种方法,可以随机打乱给定数组的元素顺序。 方法解析 接下来,我们来分析一下如何实现这种方法。一种简单的方式是通过 Fisher–Yates 洗牌算法(也称为 Knuth 洗牌算法)来实现。该算法通常被认为是一种非常高效的打…

    Java 2023年5月26日
    00
  • Spring boot基于JPA访问MySQL数据库的实现

    让我来给你讲解一下如何使用Spring Boot基于JPA访问MySQL数据库的实现以及示例。整个攻略将分为以下几个步骤: 创建一个Spring Boot项目 添加依赖 配置application.properties 建立实体类 建立Repository 建立Service 建立Controller 运行项目 下面是各个步骤的详细讲解: 1. 创建一个Sp…

    Java 2023年5月20日
    00
  • Java中使用JDBC操作数据库简单实例

    下面是详细的Java中使用JDBC操作数据库的完整攻略。 1. JDBC简介 Java Database Connectivity (JDBC) 是一种能够让Java程序与各种数据库进行连接、查询、更新、插入和删除等操作的API。使用JDBC API进行数据库操作的通常步骤如下: 加载JDBC驱动程序。 建立JDBC连接。 创建JDBC Statement或…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部