前言
今天打算用Jwt给我的微服务毕设进行权限认证,就打算配一个拦截器在网关服务进行拦截,验证Jwt,如果不知道Jwt是什么的小伙伴可以先进行相关知识学习。
存在问题
如果用的是springboot的小伙伴可以直接使用拦截器进行配置,可以忽略这个问题,可以看其他文章直接配置拦截器即可
为什么使用过滤器而不是拦截器
刚开始我是采用拦截器进行配置,但是拦截器需要导入对应的org.springframework.web包,才能实现HandlerInterceptor这个接口,等我配好之后呢,发现网关服务无法启动,不知道是什么原因,可能存在冲突,去掉对应的拦截器所需的包才能启动,寻思着换过滤器吧,反正功能一样。
GateWay配置过滤器
1.导入 Jwt包
可以在公共包中导入
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
2.添加一个工具类
public class JWTUtil {
/**
* 密钥要⾃⼰保管好
*/
private static String SECRET = "privatekey#^&^%!save";
/**
* 传⼊payload信息获取token
*
* @param map payload
* @return token
*/
public static String getToken(Map<String, String> map) {
JWTCreator.Builder builder = JWT.create();
//payload
map.forEach(builder::withClaim);
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE, 3); //默认3天过期
builder.withExpiresAt(instance.getTime());//指定令牌的过期时间
return builder.sign(Algorithm.HMAC256(SECRET));
}
/**
* 验证token
*/
public static DecodedJWT verify(String token) {
//如果有任何验证异常,此处都会抛出异常
return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
}
/**
* 获取token中的payload
*/
public static Map<String, Claim> getPayloadFromToken(String token) {
return
JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token).getClaims();
}
}
3.新建一个过滤器
@Component
public class JWTFilter implements GlobalFilter, Ordered {
/**
* 核心过滤方法:业务处理
*
* @param exchange:请求上下文(获取request和response)
* @param chain:过滤器链(控制程序放行)
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//获取请求头中的令牌
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//1、获取当前的请求连接
String path = request.getURI().getPath();
//2、判断,此请求地址是否需要进行token检验,如果不需要,直接放行,进入微服务
if (path.contains("/api/member/register")) { //包含则需要拦截
R r = new R();
//获取请求头参数token
String token = request.getHeaders().getFirst("Authorization");
try {
JWTUtil.verify(token);//验证令牌
chain.filter(exchange);//放行
} catch (SignatureVerificationException e) {
e.printStackTrace();
r = R.error(BizCodeEnume.INVALID_SIGN_EXCEPTION.getCode(), BizCodeEnume.INVALID_SIGN_EXCEPTION.getMsg()); //无效签名
} catch (TokenExpiredException e) {
e.printStackTrace();
r = R.error(BizCodeEnume.TOKEN_EXPIRE_EXCEPTION.getCode(), BizCodeEnume.TOKEN_EXPIRE_EXCEPTION.getMsg()); //token过期
} catch (AlgorithmMismatchException e) {
e.printStackTrace();
r = R.error(BizCodeEnume.TOKEN_DIFFER_EXCEPTION.getCode(),BizCodeEnume.TOKEN_DIFFER_EXCEPTION.getMsg()); //算法不一致
} catch (Exception e) {
e.printStackTrace();
r = R.error(BizCodeEnume.TOKEN_INVALID_EXCEPTION.getCode(),BizCodeEnume.TOKEN_INVALID_EXCEPTION.getMsg()); //token无效
}
//3.3作JSON转换
byte[] bytes = JSON.toJSONString(r).getBytes(StandardCharsets.UTF_8);
//3.4调用bufferFactory方法,生成DataBuffer对象
DataBuffer buffer = response.bufferFactory().wrap(bytes);
//指定编码,否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
//4.调用Mono中的just方法,返回要写给前端的JSON数据
return response.writeWith(Mono.just(buffer));
} else {
return chain.filter(exchange);
}
}
@Override
public int getOrder() {
return 0;
}
}
4.生成jwt令牌
业务完成后可以生成jwt令牌,放入到响应体中
@ApiOperation(value = "用户密码登录")
@PostMapping("/login")
public R login(HttpServletResponse response, @RequestBody MemberLoginVo vo) {
MemberEntity entity = memberService.login(vo);
if (entity != null) {
//生成JWT令牌
Map<String, String> payload = new HashMap<>();
payload.put("id", entity.getId().toString());
payload.put("username", entity.getUsername());
//生成Token
String token = JWTUtil.getToken(payload);
response.setHeader("Authorization",token);//存到响应体
response.setHeader("Access-Control-Expose-Headers", "Authorization");
return R.ok();
} else {
return R.error(BizCodeEnume.LOGINACCT_PASSWORD_INVAILD_EXCEPTION.getCode(), BizCodeEnume.LOGINACCT_PASSWORD_INVAILD_EXCEPTION.getMsg());
}
}
4.最后
最后可以去试一下效果了,j将jwt存到响应体中,还需要前端把状态存起来,请求的时候还要带上token回来验证jwt,暂时先做到这里,前端怎么做还不太熟。