思路
所有请求都会经过服务网关,服务网关对外暴露服务,在网关进行统一用户认证;
既然要在网关进行用户认证,网关得知道对哪些url进行认证,所以我们得对ur制定规则
Api接口异步请求的,我们采取url规则匹配,如:/api//auth/,如凡是满足该规则的都必须用户认证
在服务网关中加上过滤器,过滤请求,进行认证请求
对下面Filter代码进行解释:
用户登录后,返回通过jwt生成的token,返回个前端,在token的有效时间内,用户访问需要认证的接口时,通过getUserId,拿到token中的用户id,如果不为null,则通过认证,如果为null则返回错误异常
/**
* 全局Filter,统一处理会员登录与外部不允许访问的服务
* <p>
* 创建时间: 2022-03-21 19:07
*
* @author fuxshen
* @version v1.0.0
* @since v1.0.0
*/
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
System.out.println("==="+path);
//内部服务接口,不允许外部访问
if(antPathMatcher.match("/**/inner/**", path)) {
ServerHttpResponse response = exchange.getResponse();
return out(response, ResultCodeEnum.PERMISSION);
}
//api接口,异步请求,校验用户必须登录
if(antPathMatcher.match("/api/**/auth/**", path)) {
Long userId = this.getUserId(request);
if(StringUtils.isEmpty(userId)) {
ServerHttpResponse response = exchange.getResponse();
return out(response, ResultCodeEnum.LOGIN_AUTH);
}
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
/**
* api接口鉴权失败返回数据
* @param response
* @return
*/
private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
Result result = Result.build(null, resultCodeEnum);
byte[] bits = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
//指定编码,否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
}
/**
* 获取当前登录用户id
* @param request
* @return
*/
private Long getUserId(ServerHttpRequest request) {
String token = "";
List<String> tokenList = request.getHeaders().get("token");
if(null != tokenList) {
token = tokenList.get(0);
}
if(!StringUtils.isEmpty(token)) {
return JwtHelper.getUserId(token);
}
return null;
}
}
jwt工具类
注:此工具类主要用于生成token
/**
* jwt工具类
* <p>
* 创建时间: 2022-03-20 17:14
*
* @author fuxshen
* @version v1.0.0
* @since v1.0.0
*/
public class JwtHelper {
//token的过期时间
private static long tokenExpiration = 24 * 60 * 60 * 1000;
//token签名秘钥
private static String tokenSignKey = "123456";
/**
* 根据参数生成token
*
* @param userId
* @param userName
* @return java.lang.String
* @Description
* @author fuxshen
* @date 2022-03-20 17:17:15
**/
public static String createToken(Long userId, String userName) {
String token = Jwts.builder()
.setSubject("YYGH-USER")
//设置过期时间(当前系统时间+设置的时间) 当前系统时间,往后延迟我们设置的时间
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
//加密的主体信息
.claim("userId", userId)
.claim("userName", userName)
//签名hash
.signWith(SignatureAlgorithm.HS512, tokenSignKey)
.compressWith(CompressionCodecs.GZIP).compact();
return token;
}
/**
* 根据token字符串,得到用户id
*
* @param token
* @return java.lang.Long
* @Description
* @author fuxshen
* @date 2022-03-20 17:18:15
**/
public static Long getUserId(String token) {
if (StringUtils.isEmpty(token)) return null;
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
Integer userId = (Integer) claims.get("userId");
return userId.longValue();
}
/**
* 根据token字符串,得到用户名称
*
* @param token
* @return java.lang.String
* @Description
* @author fuxshen
* @date 2022-03-20 17:19:01
**/
public static String getUserName(String token) {
if (StringUtils.isEmpty(token)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return (String) claims.get("userName");
}
public static void main(String[] args) {
String token = JwtHelper.createToken(1L, "55");
System.out.println(token);
System.out.println(JwtHelper.getUserId(token));
System.out.println(JwtHelper.getUserName(token));
}
}
状态码返回的枚举类型
注:下面枚举可根据业务需要自定定义
/**
* 统一返回结果状态信息枚举
* <p>
* 创建时间: 2022-02-09 14:50
*
* @author v_fuxshen
* @version v1.0.0
* @since v1.0.0
*/
@Getter
public enum ResultCodeEnum {
LOGIN_AUTH(208, "未登陆"),
PERMISSION(209, "没有权限"),
private Integer code;
private String message;
private ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
如果需要整合前端下面是前端内容
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import cookie from 'js-cookie'
// 创建axios实例
const service = axios.create({
baseURL: 'http://localhost',
timeout: 15000 // 请求超时时间
})
// http request 拦截器
service.interceptors.request.use(
config => {
//判断cookie是否有token值
if (cookie.get('token')) {
//token值放到cookie里面
config.headers['token'] = cookie.get('token')
}
return config
},
err => {
return Promise.reject(err)
})
// http response 拦截器
service.interceptors.response.use(
response => {
//状态码是208
if (response.data.code === 208) {
//弹出登录输入框
eventLogin.$emit('loginDialogEvent')
return
} else {
if (response.data.code !== 200) {
Message({
message: response.data.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(response.data)
} else {
return response.data
}
}
},
error => {
return Promise.reject(error.response)
})
export default service