3注解,优雅实现微服务认证Authentication认证。不清楚的可以看之前的文章:实用干货!SpringCloudGateway集成OAuth2.0实现分布式统一认证授权!最近订阅《Spring Cloud Alibaba 实战》视频专栏的读者经常会问小陈两个问题,分别是:如何在每个微服务中放鉴权?feign的调用是如何实现鉴权的?今天,我们将深入探讨如何通过三个注解来解决上述两个问题。实现思路在之前的文章中,陈老师将鉴权和鉴权统一放在了网关层面。架构如下:微服务中认证还有另外一种思路:将认证交给下游的微服务,网关层面只做路由和转发。这个想法其实实现起来很简单。下面网关级认证的代码修改即可完成:实用干货!SpringCloudGateway集成OAuth2.0实现分布式统一认证授权!1.去掉认证管理器ReactiveAuthorizationManager,网关统一认证时实际依赖的。所有的请求都需要经过认证管理器来认证登录用户的权限。这个认证管理器在网关认证一文中也有介绍。在Chen的《Spring Cloud Alibaba 实战》中配置拦截也很简单,如下:除了配置的白名单,其他所有请求都必须被网关的认证管理器拦截。拦截鉴权,只有通过鉴权才能放行路由转发给下游服务。看到这里思路不是很清楚,如果想把认证交给下游服务,只需要在网关层直接发布即可,不需要经过认证管理器,代码如下:http....//白名单直接发布。pathMatchers(ArrayUtil.toArray(whiteUrls.getUrls(),String.class)).permitAll()//其他请求直接放行。anyExchange().permitAll().....2、通过第一个Step1定义了三个注解,鉴权已经委托给了下游服务,那么下游服务如何进行拦截和鉴权呢?其实SpringSecurity提供了三个用于控制权限的注解,如下:@Secured@PreAuthorize@PostAuthorize这三个注解就不详细介绍了,有兴趣的可以参考官方文档。Chen这里不打算使用内置的三个注解,而是自定义三个注解,如下:1.@RequiresLogin众所周知,只有用户登录才能发布,代码如下:/***@author公众号:码猿技术专栏*@url:www.java-family.cn*@description登录认证注意事项,在controller方法上标注,必须是登录后才能访问的接口*/@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD,ElementType.TYPE})public@interfaceRequiresLogin{}2.@RequiresPermissions看名字,只有指定权限才能释放,代码如下:/***@author公众号:码猿技术专栏*@url:www.java-family.cn*@description标注在controller方法上,确保你有指定权限访问接口*/@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD,ElementType.TYPE})public@interfaceRequiresPermissions{/***待验证权限码*/String[]value()default{};/***验证方式:AND|OR,defaultAND*/Logicallogical()defaultLogical.AND;}3.@RequiresRoles看名字就知道意思,只能??释放指定的角色,代码如下:/***@author公众号:码猿技术专栏*@url:www.java-family.cn*@description标注在controller方法上,确保你有指定角色访问接口*/@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD,ElementType.TYPE})public@interfaceRequiresRoles{/***需要验证的角色ID,默认超级管理员和管理员*/String[]value()default{OAuthConstant.ROLE_ROOT_CODE,OAuthConstant.ROLE_ADMIN_CODE};/***验证逻辑:AND|OR,defaultAND*/Logicallogical()defaultLogical.AND;}上面三个注解的意思想必很容易理解,这里就不解释了...3.Annotationsectiondefinitionannotations是有的,那么如何拦截呢?这里Chen定义了一个切面进行拦截,关键代码如下:/***@author公众号:码猿科技专栏*@url:www.java-family.cn*@description@RequiresLogin,@RequiresPermissions,@RequiresRoles注解方面*/@Aspect@ComponentpublicclassPreAuthorizeAspect{/***构造*/publicPreAuthorizeAspect(){}/***定义AOP签名(使用认证注解切入所有方法)*/publicstaticfinalStringPOINTCUT_SIGN="@annotation(com.mugu.blog.common.annotation.RequiresLogin)||"+"@annotation(com.mugu.blog.common.annotation.RequiresPermissions)||"+"@annotation(com.mugu.blog.common.annotation.RequiresRoles)";/***声明AOP签名*/@Pointcut(POINTCUT_SIGN)publicvoidpointcut(){}/***环绕切割**@paramjoinPoint切面对象*@return底层方法执行后的返回值*@throws底层方法抛出的可抛异常*/@Around("pointcut()")publicObjectaround(ProceedingJoinPointjoinPoint)throwsThrowable{//注解认证MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();checkMethodAnnotation(signature.getMethod());try{//执行原来的逻辑Objectobj=joinPoint.proceed();返回对象;}catch(Throwablee){抛出e;}}/***Method对象的注解检查*/publicvoidcheckMethodAnnotation(Methodmethod){//验证@RequiresLogin注解if(requiresLogin!=null){doCheckLogin();}//检查@RequiresRoles注解RequiresRolesrequiresRoles=method.getAnnotation(RequiresRoles.class);如果(需要角色!=null){doCheckRole(requiresRoles);}//检查@RequiresPermissions注解RequiresPermissionsrequiresPermissions=method.getAnnotation(RequiresPermissions.class);如果(requiresPermissions!=null){doCheckPermissions(requiresPermissions);}}/***检查是否登录*/privatevoiddoCheckLogin(){LoginValloginVal=SecurityContextHolder.get();如果(Objects.isNull(loginVal))抛出新的ServiceException(ResultCode.INVALID_TOKEN.getCode(),ResultCode.INVALID_TOKEN.getMsg());}/***检查是否有对应的角色*/privatevoiddoCheckRole(RequiresRolesrequiresRoles){String[]roles=requiresRoles.value();LoginValloginVal=OauthUtils.getCurrentUser();//登录用户对应的RoleString[]authorities=loginVal.getAuthorities();布尔匹配=假;//和逻辑if(requiresRoles.logical()==Logical.AND){match=Arrays.stream(authorities).filter(StrUtil::isNotBlank).allMatch(item->CollectionUtil.contains(Arrays.asList(roles),item));}else{//OR逻辑匹配=Arrays.stream(authorities).filter(StrUtil::isNotBlank).anyMatch(item->CollectionUtil.contains(Arrays.asList(roles),item));}if(!match)thrownewServiceException(ResultCode.NO_PERMISSION.getCode(),ResultCode.NO_PERMISSION.getMsg());}/***TODO自己实现,由于没有集成前端菜单权限,所以根据业务需要自己实现*/privatevoiddoCheckPermissions(RequiresPermissionsrequiresPermissions){}}其实就是中间的逻辑很简单,就是解析Token中的权限和角色,然后与注解中指定的进行比较@RequiresPermissions注解的逻辑,陈先生并没有实现。他根据业务模仿它。是思考的问题...4.注解使用比如《Spring Cloud Alibaba 实战》项目中有一个添加文章的接口,只能添加超级管理和管理成员角色,那么可以使用@RequiresRoles注解进行标记,如下:@RequiresRoles@AvoidRepeatableCommit@ApiOperation("AddArticle")@PostMapping("/add")publicResultMsg>listTotal(@RequestBody@ValidList
