{
“sub”: “1234567890”,
“name”: “John Doe”,
“admin”: true
}
注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。
这个 JSON 对象也要使用 Base64URL 算法转成字符串。
Signature 部分是对前两部分的签名,其目的是防止数据被篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。
=================================================================
第一步:创建项目,例如:
第二步,添加依赖,其pom.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?><project xmlns=“http://maven.apache.org/POM/4.0.0”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>
4.0.0
spring-boot-starter-parent
org.springframework.boot
2.3.2.RELEASE
com.cy
03-jt-security-jwt
1.0-SNAPSHOT
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
io.jsonwebtoken
jjwt
0.9.1
第三步:创建配置文件application.yml (暂时不写任何内容)
第四步:定义启动类,代码如下
package com.cy.jt;
@SpringBootApplication
public class SecurityJwtApplication {
public static void main(String[] args) {
SpringApplication.run(
SecurityJwtApplication.class,
args);
}
}
第四步:运行启动类,检测是否可成功启动
编写单元测试,实践Token对象的创建与解析,例如:
@Test
void testCreateAndParseToken(){
//1.创建令牌
//1.1定义负载信息
Map<String,Object> map=new HashMap<>();
map.put(“username”, “jack”);
map.put(“permissions”, “sys:res:create,sys:res:retrieve”);
//1.2定义过期实践
Calendar calendar=Calendar.getInstance();
calendar.add(Calendar.MINUTE, 30);
Date expirationTime=calendar.getTime();
//1.3定义密钥
String secret=“AAABBBCCCDDD”;
//1.4生成令牌
String token= Jwts.builder()
.setClaims(map)
.setIssuedAt(new Date())
.setExpiration(calendar.getTime())
.signWith(SignatureAlgorithm.HS256,secret)
.compact();
System.out.println(token);
//2.解析令牌
Claims claims = Jwts.parser().setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
System.out.println(“claims=”+claims);
}
为了简化JWT在项目中的应用,我们通常会构建一个工具类,对token的创建和解析进行封装,例如:
package com.cy.jt.security.util;
public class JwtUtils {
/**
- 秘钥
*/
private static String secret=“AAABBBCCCDDDEEE”;
/**
-
有效期,单位秒
-
默认30分钟
*/
private static Long expirationTimeInSecond=1800L;
/**
-
从token中获取claim
-
@param token token
-
@return claim
*/
public static Claims getClaimsFromToken(String token) {
try {
return Jwts.parser()
.setSigningKey(secret.getBytes())
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
throw new IllegalArgumentException(“Token invalided.”);
}
}
/**
-
判断token是否过期
-
@param token token
-
@return 已过期返回true,未过期返回false
*/
private static Boolean isTokenExpired(String token) {
Date expiration = getClaimsFromToken(token).getExpiration();
return expiration.before(new Date());
}
/**
-
为指定用户生成token
-
@param claims 用户信息
-
@return token
*/
public static String generateToken(Map<String, Object> claims) {
Date createdTime = new Date();
Date expirationTime = new Date(System.currentTimeMillis() + expirationTimeInSecond * 1000);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(createdTime)
.setExpiration(expirationTime)
.signWith(SignatureAlgorithm.HS256,secret)
.compact();
}
}
====================================================================
AuthController 认证服务
定义AuthController用于处理登录认证业务,代码如下:
package com.cy.jt.security.controller;
@RestController
public class AuthController {
@RequestMapping(“/login”)
public Map<String,Object> doLogin(String username,
String password){
Map<String,Object> map=new HashMap<>();
if(“jack”.equals(username)&&“123456”.equals(password)){
map.put(“state”,“200”);
map.put(“message”,“login ok”);
Map<String,Object> claims=new HashMap<>();//负载信息
claims.put(“username”,username);
map.put(“Authentication”, JwtUtils.generatorToken(claims));
return map;
}else{
map.put(“state”,“500”);
map.put(“message”,“login failure”);
return map;
}
}
}
ResourceController 资源服务
定义一个资源服务对象,登录成功以后可以访问此对象中的方法,例如:
package com.cy.jt.security.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ResourceController {
@RequestMapping(“/retrieve”)
public String doRetrieve(){
//检查用户有没有登录
//执行业务查询操作
return “do retrieve resource success”;
}
@RequestMapping(“/update”)
public String doUpdate(){
//检查用户有没有登录
//执行业务查询操作
return “do update resource success”;
}
}
TokenInterceptor 拦截器及配置
假如在每个方法中都去校验用户身份的合法性,代码冗余会比较大,我们可以写一个Spring MVC 拦截器,
在拦截器中进行用户身份检测,例如:
package com.cy.jt.security.interceptor;
import com.cy.jt.security.util.JwtUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
-
令牌(token:ticker-通票)拦截器
-
其中,HandlerInterceptor为Spring MVC中的拦截器,
-
可以在Controller方法执行之前之后执行一些动作.
-
1)Handler 处理器(Spring MVC中将@RestController描述的类看成是处理器)
-
2)Interceptor 拦截器
*/
public class TokenInterceptor implements HandlerInterceptor {
/**
-
preHandle在目标Controller方法执行之前执行
-
@param handler 目标Controller对象
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
//http://localhost:8080/retrieve?Authentication=
//String token= request.getParameter(“Authentication”);
String token=request.getHeader(“Authentication”);
eptor 拦截器
*/
public class TokenInterceptor implements HandlerInterceptor {
[外链图片转存中…(img-KKNN3WDH-1716193433106)]
/**
-
preHandle在目标Controller方法执行之前执行
-
@param handler 目标Controller对象
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
//http://localhost:8080/retrieve?Authentication=
//String token= request.getParameter(“Authentication”);
String token=request.getHeader(“Authentication”);