这里是关于如何基于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技术站