1、首先引入jjwt的依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2、编写生成token的工具类
package com.jjw.result.util;
import com.jjw.result.constants.SystemConstants;
import io.jsonwebtoken.*;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;
public class AppJwtUtil {
// TOKEN的有效期一天(S)
private static final int TOKEN_TIME_OUT = 3600;
// 加密KEY
private static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";
// 最小刷新间隔(S)
private static final int REFRESH_TIME = 300;
// 生产ID
public static String createToken(Long id) {
Map<String, Object> claimMaps = new HashMap<>();
claimMaps.put("id", id);
long currentTime = System.currentTimeMillis();
return Jwts.builder()
.setId(UUID.randomUUID().toString())
.setIssuedAt(new Date(currentTime)) //签发时间
.setSubject("system") //说明
.setIssuer("jjw") //签发者信息
.setAudience("app") //接收用户
.compressWith(CompressionCodecs.GZIP) //数据压缩方式
.signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
//过期一个小时
.setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //过期时间戳
.addClaims(claimMaps) //cla信息
.compact();
}
/**
* 获取token中的claims信息
*
* @param token
* @return
*/
private static Jws<Claims> getJws(String token) {
return Jwts.parser()
.setSigningKey(generalKey())
.parseClaimsJws(token);
}
/**
* 获取payload body信息
*
* @param token
* @return
*/
public static Claims getClaimsBody(String token) {
try {
return getJws(token).getBody();
} catch (ExpiredJwtException e) {
return null;
}
}
/**
* 获取hearder body信息
*
* @param token
* @return
*/
public static JwsHeader getHeaderBody(String token) {
return getJws(token).getHeader();
}
/**
* 是否过期
*
* @param token
* @return 1 有效 0 无效 2 已过期
*/
public static Integer verifyToken(String token) {
try {
Claims claims = AppJwtUtil.getClaimsBody(token);
if (claims == null) {
return SystemConstants.JWT_FAIL;
}
return SystemConstants.JWT_OK;
} catch (ExpiredJwtException ex) {
return SystemConstants.JWT_EXPIRE;
} catch (Exception e) {
return SystemConstants.JWT_FAIL;
}
}
/**
* 由字符串生成加密key
*
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
public static void main(String[] args) {
/* Map map = new HashMap();
map.put("id","11");*/
String token = AppJwtUtil.createToken(1102L);
System.out.println(token);
Claims claims = AppJwtUtil.getClaimsBody(token);
//Integer integer = AppJwtUtil.verifyToken("dsafafsa");
Integer integer = AppJwtUtil.verifyToken("eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAAC2LywrDIBAA_2XP8aCrZs3frI-CQkFYQ1tK_z0b6G2GYb4wVocDMNuMXMhQfKDxJQXDuEcTk68tuFooBNig84LDRiJHO2HaQM6st3xktefdRVTHeCnzWZV5TuX2nv8vWX9_XZv7Xf0W2Rt-AAAA.srh9GIXr4ZqpXNfFb6vGIUxT12ve6Tu6xQ2KmdySsziP6AeCgP1h2GBhhx1g4rNbdsLozNI_WA-2FKsoR6-yPg");
System.out.println(integer);
System.out.println(claims);
}
}
3、使用到的常量类:
package com.jjw.result.constants;
public class SystemConstants {
//JWT TOKEN已过期
public static final Integer JWT_EXPIRE = 2;
//JWT TOKEN有效
public static final Integer JWT_OK = 1;
//JWT TOKEN无效
public static final Integer JWT_FAIL = 0;
public static final String USER_HEADER_NAME = "userId";
public static final Integer TYPE_USER = 1; // 用户
public static final Integer TYPE_E = 0; // 设备
}
4、在service的实现类中调用,生成token返回给前端
//4.生成令牌返回给前端
String token = AppJwtUtil.createToken(Long.valueOf(adUserFromDb.getId()));
Map<String, Object> info = new HashMap<>();
info.put("token",token);
5、在网关上进行校验,因此需要搭建网关服务。首先引人依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.jjw</groupId>
<artifactId>result-content</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
<!--要排除spring-boot-starter-web这个依赖,但是result-content中包含的swagger-content这个
依赖又依赖于spring-boot-starter-web,而我只需要result-content中的内容,所以将这两个依赖都排除了-->
<exclusion>
<groupId>com.jjw</groupId>
<artifactId>swagger-content</artifactId>
</exclusion>
</exclusions>
</dependency>
6、启动类代码如下:
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayAdminApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayAdminApplication.class,args);
}
}
7、配置application.yml文件
spring:
profiles:
active: dev
---
server:
port: 6001
spring:
application:
name: leadnews-admin-gateway
profiles: dev
cloud:
nacos:
server-addr: 192.168.211.136:8848
discovery:
server-addr: ${spring.cloud.nacos.server-addr}
gateway:
globalcors:
add-to-simple-url-handler-mapping: true #增加
cors-configurations:
'[/**]': # 匹配所有请求
allowedOrigins: "*" #跨域处理 允许所有的域
allowedHeaders: "*"
allowedMethods: # 支持的方法
- GET
- POST
- PUT
- DELETE
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
routes:
# 平台管理
- id: admin
uri: lb://leadnews-admin
predicates:
- Path=/admin/**
filters:
- StripPrefix= 1
8、添加网关模块、结构如下:
文章来源地址https://www.toymoban.com/news/detail-529760.html
9、编写校验token的AuthorizeFilter类(全局过滤器),其内容如下:
package com.jjw.gatewayadmin.filter;
import com.jjw.result.constants.SystemConstants;
import com.jjw.result.util.AppJwtUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//+ 1.先获取请求和响应对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// /get /a post /a
//+ 2.判断当前的请求 是否 是登录的请求 如果是 ,则放行
String path = request.getURI().getPath();
if(path.equals("/admin/admin/login")){
return chain.filter(exchange);
}
//+ 3.获取页面传递过来的请求头中的令牌数据 如果获取不到 返回错误(401)
String token = request.getHeaders().getFirst("token");
if(StringUtils.isEmpty(token)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();//完成响应 返回 后面就不执行了
}
//+ 4.校验令牌 校验失败 返回错误401
//通过jwt 来校验
if(SystemConstants.JWT_OK!= AppJwtUtil.verifyToken(token)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();//完成响应 返回 后面就不执行了
}
//+ 5.校验成功 放行
return chain.filter(exchange);
}
//过滤器的执行的优先级的设置 值越低 优先级越高
@Override
public int getOrder() {
return 0;
}
}
可在请求头中添加用户信息:
import com.alibaba.fastjson.JSON;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URLEncoder;
/**
* 全局过滤器:所有的请求都需要经过此过滤器。可以自定义业务逻辑进行处理,在转发请求之前先处理
* 步骤如下:
* 1、创建一个类实现GlobalFilter, Ordered
* 2、将此类交给spring容器进行管理 @Component
* 3、重写方法,实现自定义的业务逻辑
*/
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
//获取用户携带的token令牌 解析校验 校验通过放行 不通过 返回错误
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取请求对象 和 响应对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//1.5 如果请求的路径是 自媒体登录【白名单】 放行即可
String path = request.getURI().getPath();
if(path.startsWith("/user/app/login") ||path.startsWith("/user/app/refreshToken") || path.endsWith("v2/api-docs")){
return chain.filter(exchange);
}
//2.从请求头中获取访问令牌数据
String token = request.getHeaders().getFirst("token");
//3.判断 是否为空 如果为空 返回错误 401
if(StringUtils.isEmpty(token)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//4.校验令牌是否正确了 如果不是 返回错误 401
try {
// 4.1 解析成功令牌有效
UserTokenInfoExp userTokenInfoExp = JwtUtil.parseJwtUserToken(token);
// 4.2 判断角色是否正确(网关是App网关, 来的令牌必须是APP或者是匿名用户的令牌)
if(JwtUtil.isValidRole(userTokenInfoExp, TokenRole.ROLE_MEDIA)||JwtUtil.isValidRole(userTokenInfoExp, TokenRole.ROLE_ADMIN)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//直接返回 表示需要续约 判断是否过期
if(JwtUtil.isExpire(userTokenInfoExp)){
//403
response.setStatusCode(HttpStatus.FORBIDDEN);
return response.setComplete();
}
// 成功了将用户的令牌解析之后的所有的数据 放到请求头中 传递给下游 放行
//URL编码 否则有乱码产生
String encode = URLEncoder.encode(JSON.toJSONString(userTokenInfoExp), "UTF-8");
//将信息传递给下游微服务
request.mutate().header(SystemConstants.USER_HEADER_NAME, encode);
} catch (Exception e) {
e.printStackTrace();
//直接错误
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
}
//值越低 优先级越高 优先被执行
@Override
public int getOrder() {
return -10;
}
校验当前用户发送过来的请求是否有权限去访问,用户需要在请求头中携带之前生成颁发的令牌过来
//@Override
//public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// //+ 1.先获取请求和响应对象
// ServerHttpRequest request = exchange.getRequest();
//
// ServerHttpResponse response = exchange.getResponse();
// // /get /a post /a
//
// //+ 2.判断当前的请求 是否 是登录的请求 如果是 ,则放行
// String path = request.getURI().getPath();
// if(path.equals("/media/wmUser/login")){
// return chain.filter(exchange);
// }
//
// //+ 3.获取页面传递过来的请求头中的令牌数据 如果获取不到 返回错误(401)
// String token = request.getHeaders().getFirst("token");
// if(StringUtils.isEmpty(token)){
// response.setStatusCode(HttpStatus.UNAUTHORIZED);
// return response.setComplete();//完成响应 返回 后面就不执行了
// }
//
// //+ 4.校验令牌 校验失败 返回错误401
// //通过jwt 来校验
// if(SystemConstants.JWT_OK!= AppJwtUtil.verifyToken(token)){
// response.setStatusCode(HttpStatus.UNAUTHORIZED);
// return response.setComplete();//完成响应 返回 后面就不执行了
// }
//
// //在路由请求之前,将当前令牌中的用户id解析出来并添加到请求头中,传递给下游
// Claims claimsBody = AppJwtUtil.getClaimsBody(token);
// Object id = claimsBody.get("id"); // 这个id是claimMaps.put("id", id);的id
// //String id1 = claimsBody.getId(); // 这俩id是不一样的,这个id是Jwts.builder().setId
//
// request.mutate().header(USER_HEADER_NAME, id.toString());
//
// //+ 5.校验成功 放行
// return chain.filter(exchange);
//}
//
//
过滤器的执行的优先级的设置 值越低 优先级越高
//@Override
//public int getOrder() {
// return 0;
//}
}
在服务端如何获取拦截器放在请求头中的信息:先定义一个获取的工具类
public class RequestContextUtil {
/**
* 获取登录的用户的ID 可以是自媒体账号 也可以是 平台账号 也可以是app账号
* @return
*/
public static String getUserInfo(){
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
//获取路由转发的头信息
String headerValue = request.getHeader(SystemConstants.USER_HEADER_NAME);
return headerValue;
}
}
在微服务中使用到的地方直接导包调用即可:
String userId = RequestContextUtil.getUserInfo();
文章来源:https://www.toymoban.com/news/detail-529760.html
到了这里,关于java jwt生成token并在网关设置全局过滤器进行token的校验并在给请求头设置参数及在微服务中解析参数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!