AOP鉴权 或 拦截器/过滤器鉴权
这个方法是利用Spring-AOP的机制,进行鉴权,可以使用execution进行鉴权
也可以使用@annotion进行鉴权,方式很多
这里再来说说网关鉴权
GateWay鉴权
我们可以在网关层面进行鉴权,在这个时候防止流量下放
在最开始的时候接口进入鉴权白名单,我们可以直接放行,在各个服务进行鉴权
代码语言:javascript复制 @Around("@annotation(authCheck)")
public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
// 获取管理信息
String mustRole = authCheck.mustRole();
List<String> anyRole = Arrays.stream(authCheck.anyRole())
.filter(StringUtils::isNotBlank)
.collect(Collectors.toList());
Boolean checkLogin = authCheck.checkLogin();
// 获取请求信息
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
// 获取路由信息
String method = request.getMethod().toUpperCase(Locale.ROOT);
String requestURI = request.getRequestURI();
String contextPath = request.getContextPath();
// 判断在gateway上是否鉴权
String authed = request.getHeader("Authed");
if (!"gateway".equals(authed)) {
log.info("n新注册到认证中心的接口,路由信息如下:n"
"method: [{}]n"
"requestURI: [{}]n"
"contextPath: [{}]",method,requestURI,contextPath);
// 通过rpc获取 认证中心 的 route,曾经是否注册过
Auth one = authService.getOne(method, requestURI, contextPath);
// 如果是新增的路由,那么就加上
if (ObjectUtils.isEmpty(one)) {
one = new Auth()
.setMethod(method)
.setUri(requestURI)
.setRoute(contextPath)
.setIsDef(true)
.setOpen(true);
}
// 必须有该权限才通过
if (StringUtils.isNotBlank(mustRole)) {
if (!anyRole.contains(mustRole)) {
anyRole.add(mustRole);
}
}
one.setOpen(true).setIsDef(true).setAuthRoles(anyRole).setCheckLogin(checkLogin).setIsDef(true); //设置成真值,一样的交给网关执行
boolean state = authService.saveOrUpdate(one);
log.info("认证中心保存更新状态state: {}",state);
// 网关没有鉴权,那么在这里鉴权
log.info("AOP鉴权开始");
String token = request.getHeader(CommonConstant.TOKEN_HEADER);
User userFromToken = AuthUtil.getUserFromToken(token);
if (ObjectUtils.isEmpty(userFromToken)) {
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "未登录或者状态过期,请能够重新登录!");
}
if (checkLogin==false&&!anyRole.contains(userFromToken.getUserRole())||UserConstant.BAN_ROLE.equals(userFromToken.getUserRole())) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
}
else{
//如果已经鉴权,通过流量染色
log.info("已在gateway进行鉴权,即将进入业务层,路由信息如下n"
"method: [{}]n"
"requestURI: [{}]n"
"contextPath: [{}]",method,requestURI,contextPath);
}
// 进行响应日志记录
return handelResponse(joinPoint);
}
鉴权之前,我们通过RPC注册到鉴权中心,后续通过gateway的时候先流量脱色,再鉴权,最后再染色
代码语言:javascript复制 @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 请求
ServerHttpRequest request = exchange.getRequest();
// 响应
ServerHttpResponse response = exchange.getResponse();
// 获取路由路径
RequestPath path = request.getPath();
log.info("forward path:{}",path);
// 这里是一个重要的鉴权逻辑,防止伪造染色
exchange=exchange.mutate().request(request.mutate().headers(httpHeaders -> httpHeaders.remove("Authed") ).build()).build();
String requestMethod = request.getMethod().name();
// 如果不是get请求,那么进行重放检测
if(!HttpMethod.GET.matches(requestMethod)){
Mono<Void> voidMono = doReplayDetection(exchange);
if (ObjectUtils.isNotEmpty(voidMono)){
return voidMono;
}
}
request.mutate().headers(headers -> headers.add("x-forwarded-for",request.getRemoteAddress().getAddress().getHostAddress()));
//通过 全局鉴权容器进行遍历、匹配
for(GlobalContainer.Route v:GlobalContainer.authRouteList){
String method = v.getMethod();
String url = v.getUrl();
//当 当前转发路由、方法 和 需要鉴权的路由方法一直时,进行鉴权
log.info("url:{} path:{}",url,path);
log.info("method:{} name:{}",method,requestMethod);
if (url.equals(path.value())&&method.equals(requestMethod)){
// 进行鉴权操作
// 通过Token获取用户信息
User currentUser = AuthUtil.getUserFromToken(exchange.getRequest().getHeaders().getFirst("Authorization"));
if (ObjectUtils.isEmpty(currentUser)){
return handleOther(null,
ResultUtils.error(ErrorCode.FORBIDDEN_ERROR,"Token无效,请重新登录"),
response);
}
// 是否只需要检查登录状态
Boolean checkLogin = v.getCheckLogin();
log.info("v.getCheckLogin():{}", checkLogin);
if (BooleanUtils.isTrue(checkLogin)){
return handleResponse(exchange, chain);
}
// 获取用户权限信息
String userRole = currentUser.getUserRole();
// 被BAN了的用户
if (UserConstant.BAN_ROLE.equals(userRole)){
return handleNoAuth(response);
}
log.info("userRole:{} roles:{} user:{}",userRole,v.getRole(),currentUser);
boolean contains = v.getRole().contains(userRole);
if (!contains){
return handleNoAuth(response);
}
// 鉴权完毕,修改请求头
ServerHttpRequest build = request.mutate().headers(header-> header.add("Authed","gateway")).build();
exchange=exchange.mutate().request(build).build();
break;
}
}
// 交给响应事件
return handleResponse(exchange, chain);
}
继承HandlerMethodArgumentResolver鉴权
代码语言:javascript复制public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter var1);
@Nullable
Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;
}
这个类的作用主要是用于方法拦截
第一个supportsParmeter是拦截条件
第二个resolveArgument是执行的计划,满足supportsParmeter的话就执行resolveArgument,执行完后我们会把返回值给到满足条件的参数上面
然后我们对编写的类进行一个注册
代码语言:javascript复制@Configuration
public class WebConfig extends WebMvcConfigurerAdapter{
@Autowired
类 对象;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(对象);
}
}
所以说,可玩性很高,我们可以判断一个注解是否在某个参数上,而那个参数是用户类型,然后我们可以在resolveArgument中进行鉴权,并且将鉴权后的结果(.e.g: 登录用户对象 等)返回给参数