代码地址与接口看总目录:【学习笔记】记录冷冷-pig项目的学习过程,大概包括Authorization Server、springcloud、Mybatis Plus~~~_清晨敲代码的博客-CSDN博客
之前只零碎的学习过spring-cloud-alibaba,并没有全面了解过,这次学习pig框架时,想着可以根据这个项目学习一下,练练手,于是断断续续的用了几天时间搭建了一下基础框架。目前就先重点记录一下遇到的问题吧,毕竟流程也不是特别复杂,就是有的东西没遇到过了解的也不深~
由于微服务包括认证这里内容太多,所以分了好几篇~
第一篇文章:No6.从零搭建spring-cloud-alibaba微服务框架,实现fegin、gateway、springevent等(一)_清晨敲代码的博客-CSDN博客
文章包括:
1.将服务系统注册到nacos注册中心;
2.通过nacos实现配置动态更新;
3.添加fegin服务,实现服务之间调用;
4.添加网关(学会使用webflux,学会添加过滤器);
5.添加log服务,通过springevent实现,并使用注解使用(使用AOP);
第二篇文章:
No6.从零搭建spring-cloud-alibaba微服务框架,实现数据库调用、用户认证与授权等(二,no6-2)_清晨敲代码的博客-CSDN博客
文章包括:
6.添加 mysql 数据库调用,并使用mybatis-plus操作;
7.在认证模块添加用户认证,基于oauth2的自定义密码模式(已认证用户是基于自定义token加redis持久化,不是session);
第三篇文章:
No6-3.从零搭建spring-cloud-alibaba微服务框架,实现资源端用户认证与授权等(三,no6-3)
8.在资源端模块添加用户认证,基于授权端的认证token添加逻辑(但是没有处理微服务间的不鉴权调用,微服务间的调用接口都是白名单呢!);
本篇内容包括:
9.解决微服务间的不鉴权调用(可修改外部访问鉴权逻辑~)
剩余包括(会有变动):
10.添加用户鉴权逻辑(基于RBAC逻辑,使用springsecuiry安全注解实现)
目录
A9.解决微服务间的不鉴权调用(可修改外部访问鉴权逻辑~)
步骤:
测试:
微服务间调用问题,和 pig 项目里面的实现逻辑,在另一篇文里面总结了,就不复述了,并且修改外部访问鉴权逻辑,也在这篇文章里面可以看:【pig-cloud项目】关于@Inner和@PreAuthorize的理解,以及微服务内外部间的调用认证鉴权理解
1.添加 @Inner ;
2.修改 PermitAllUrlProperties ,再创建bean之前,记得包被@Inner注解的方法或类加进白名单中;
3.添加 PigSecurityInnerAspect ,添加微服务内部调用的 header(From) 值;并且添加到 /META-INF/string.factoies 中;
4.在 pig-gateway 模块添加PigRequestGlobalFilter全局过滤器,将请求中的 header(From) 值全部清除。
5.在需要的接口上添加 @Inner 注解;
6.给 pig-upms 模块的配置文件 application.yml 或者nacos中的配置文件中修改,springmvc接口匹配规则由默认的path_pattern_parser修改为ant_path_matcher;
//1.
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Inner {
/**
* 是否AOP统一处理
* @return false, true
*/
boolean value() default true;
/**
* 需要特殊判空的字段(预留)
* @return {}
*/
String[] field() default {};
}
//2.修改这个类,使他实现 InitializingBean 类,这样再创建 bean 之前就能够修改对他进行以下操作
@Slf4j
@ConfigurationProperties(prefix = "security.oauth2.ignore")
public class PermitAllUrlProperties implements InitializingBean {
private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
@Getter
@Setter
private List urls = new ArrayList<>();
@Override
public void afterPropertiesSet() throws Exception {
//拿到 bean ,RequestMappingHandlerMapping的作用是在容器启动后将系统中所有控制器方法的请求条件(RequestMappingInfo)和控制器方法(HandlerMethod)的对应关系注册到RequestMappingHandlerMapping Bean的内存中,
RequestMappingHandlerMapping mapping = SpringUtil.getBean("requestMappingHandlerMapping");
//拿到映射的mapper
Map map = mapping.getHandlerMethods();
map.keySet().forEach(info -> {
HandlerMethod handlerMethod = map.get(info);
// 获取方法上边的注解 替代path variable 为 *。这里注意,如果是 /user/{id} 修改为: /user/* ,如果是 /user/id 还是: /user/id
//todo 必须注意,如果是 /{id} 会修改为: /* ,就相当于,当前类同级下的匹配路径都会被添加到ignore-urls中!!!!
Inner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class);
Optional.ofNullable(method).ifPresent(inner -> info.getPatternsCondition().getPatterns()
.forEach(url -> urls.add(ReUtil.replaceAll(url, PATTERN, "*"))));
// 获取类上边的注解, 替代path variable 为 *。这里注意,如果是 /user/{id} 修改为: /user/* ,如果是 /user/id 还是: /user/id
Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class);
Optional.ofNullable(controller).ifPresent(inner ->
info.getPatternsCondition().getPatterns().forEach(url -> urls.add(ReUtil.replaceAll(url, PATTERN, "*"))));
});
}
}
//3.@Inner 注解切面
@Slf4j
@Aspect
@RequiredArgsConstructor
public class PigSecurityInnerAspect implements Ordered {
private final HttpServletRequest request;
@SneakyThrows
@Around("@within(inner) || @annotation(inner)")
public Object around(ProceedingJoinPoint point, Inner inner) {
// 实际注入的inner实体由表达式后一个注解决定,即是方法上的@Inner注解实体,若方法上无@Inner注解,则获取类上的
if (inner == null) {
Class> clazz = point.getTarget().getClass();
inner = AnnotationUtils.findAnnotation(clazz, Inner.class);
}
//拿到 header(From) 参数值
String header = request.getHeader(SecurityConstants.FROM);
//是 aop 统一处理,并且 header(From) 值又正确,就跳过直接执行方法;否则抛出无权限异常
if (inner.value() && !StrUtil.equals(SecurityConstants.FROM_SECRET_VALUE, header)) {
log.warn("访问接口 {} 没有权限", point.getSignature().getName());
throw new AccessDeniedException("Access is denied");
}
return point.proceed();
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 1;
}
}
//--------------
//添加到 /META-INF/string.factoies
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pig4cloud.pig.common.security.service.PigRedisOAuth2AuthorizationService,\
com.pig4cloud.pig.common.security.service.PigRemoteRegisteredClientRepository,\
com.pig4cloud.pig.common.security.component.PigSecurityInnerAspect
//4.网关的全局拦截器,作用所有的微服务
public class PigRequestGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 清洗请求头中from 参数;修改请求里面的内容然后再重新构建一下
ServerHttpRequest request = exchange.getRequest().mutate()
.headers(httpHeaders -> httpHeaders.remove(SecurityConstants.FROM)).build();
//将修改后的请求放入 ServerWebExchange 里面
addOriginalRequestUrl(exchange, request.getURI());
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 10;
}
}
//5. @Inner 注解的value默认是他true表示只有远程调用才可以使用,若value=false表示任意的访问都使用
@RestController
@RequiredArgsConstructor
@RequestMapping("/user")
public class UserController {
private final SysUserService userService;
...
/**
* @Description: 获取指定用户名的用户全部信息,用于用户认证
* @param username
* @Return: com.pig4cloud.pig.common.core.util.R
*/
@Inner
@GetMapping("/info/{username}")
public R info(@PathVariable String username) {
SysUser sysUser = userService.getOne(Wrappers.lambdaQuery().eq(SysUser::getUsername, username));
if(sysUser == null){
return R.failed("用户不存在");
}
//这里拿到用户的其他关联信息,并赋值给VO
UserInfoDTO userInfoDTO = userService.getUserInfo(sysUser);
return R.ok(userInfoDTO);
}
...
}
//6.在 nacos 上的 application.yml 中添加
# Spring 相关
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
但是要注意:凡是被 @Inner 标注的接口都不会获取用户认证信息,并且会被加入 security 白名单!