点击关注公众号,实用技术文章及时了解

来源:blog.csdn.net/new_com/article/

details/104731154

在Spring Cloud Security 中,认证和授权都是通过FilterChainProxy(Servlet Filter过滤器)拦截然后进行操作的。FilterSecurityInterceptor过滤器拦截了受保护资源的请求,然后进行授权处理,授权验证的逻辑在其父类AbstractSecurityInterceptor实现。大致流程如下:

  • 使用SecurityMetadataSource根据http请求获取对应拥有的权限。

  • 使用Spring Security授权模块对用户访问的资源进行授权验证。

AbstractSecurityInterceptor的部分源码如下:

// AbstractSecurityInterceptor.javaprotected InterceptorStatusToken beforeInvocation(Object object) {......// 根据http请求获取对应的配置的权限信息Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);......// 对用户认证进行校验Authentication authenticated = authenticateIfRequired();try {// 对用户的权限与访问资源拥有的权限进行校验this.accessDecisionManager.decide(authenticated, object, attributes);}catch (AccessDeniedException accessDeniedException) {publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,accessDeniedException));throw accessDeniedException;}......}

在默认的SecurityMetadataSource的子类DefaultFilterInvocationSecurityMetadataSource实现中,会把资源服务器配置的权限信息全部加载到内存。如果要实现授权权限的动态修改,需要扩展SecurityMetadataSource,例如,使权限数据能够动态的从数据库获取。并且,自定义根据动态权限认证逻辑AccessDecisionVoter。

扩展SecurityMetadataSource

自定义PermissionFilterInvocationSecurityMetadataSource,参考默认的DefaultFilterInvocationSecurityMetadataSource实现从数据库动态的根据访问http请求获取配置的权限。由于每次都需要获取全部的有效的权限配置数据,可以对权限数据做一个本地缓存,提交查询效率。

在ConfigAttribute的子类实现中,可以使用SecurityConfig保存配置的权限. 访问规则ConfigAttribute。实现代码如下:

public class PermissionFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {private final PermissionClient permissionClient;public PermissionFilterInvocationSecurityMetadataSource(PermissionClient permissionClient) {this.permissionClient = permissionClient;}/*** 转换权限列表*/private Map<RequestMatcher, Collection<ConfigAttribute>> requestMatcherCollectionMap() {List<Permission> allPermissions = permissionClient.findAllList();if (CollectionUtils.isEmpty(allPermissions)) {return ImmutableMap.of();}return allPermissions.stream().collect(Collectors.toMap(permission -> new AntPathRequestMatcher(permission.getUrl()),permission -> Lists.newArrayList(new SecurityConfig(permission.getCode()))));}@Overridepublic Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {final HttpServletRequest request = ((FilterInvocation) object).getRequest();for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMatcherCollectionMap().entrySet()) {if (entry.getKey().matches(request)) {return entry.getValue();}}return null;}@Overridepublic Collection<ConfigAttribute> getAllConfigAttributes() {return requestMatcherCollectionMap().values().stream().flatMap(Collection::stream).collect(Collectors.toList());}@Overridepublic boolean supports(Class<?> clazz) {return FilterInvocation.class.isAssignableFrom(clazz);}
}

扩展根据权限授权逻辑

自定义PermissionsVoter类实现AccessDecisionVoter接口,实现了用户只要拥有访问资源的权限就可以访问。参考RoleVoter具体的实现逻辑,代码如下:

public class PermissionsVoter implements AccessDecisionVoter<Object> {@Overridepublic boolean supports(ConfigAttribute attribute) {return Objects.nonNull(attribute.getAttribute());}@Overridepublic boolean supports(Class<?> clazz) {return true;}@Overridepublic int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {if (CollectionUtils.isEmpty(attributes)) {return ACCESS_DENIED;}// 用户授权的权限Collection<? extends GrantedAuthority> grantedAuthorities;if (Objects.isNull(authentication)|| CollectionUtils.isEmpty(grantedAuthorities = extractAuthorities(authentication))|| Objects.isNull(object)) {log.info("user no authentication!");return ACCESS_DENIED;}for (GrantedAuthority grantedAuthority : grantedAuthorities) {String authority;if (StringUtils.isNotBlank(authority = grantedAuthority.getAuthority())&& match(authority, attributes)) {return ACCESS_GRANTED;}}return ACCESS_DENIED;}private boolean match(String authority, Collection<ConfigAttribute> attributes) {for (ConfigAttribute configAttribute : attributes) {String attribute;if (StringUtils.isNotBlank(attribute = configAttribute.getAttribute())&& attribute.equals(authority)) {return true;}}return false;}/*** 获取用户权限列表*/Collection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {return authentication.getAuthorities();}
}

配置资源服务器

在配置资源服务器,主要是如下配置:

  • SecurityMetadataSource,获取资源权限的设置

  • AccessDecisionManager,自定义授权逻辑的配置

重点讲解下自定义AccessDecisionManager的情况,

  • 选择AffirmativeBased(只要有一个授权处理通过则可以进行访问)。

  • 配置RoleVoter(角色授权),AuthenticatedVoter(认证信息授权), WebExpressionVoter(EL描述授权)spring security默认的授权逻辑。

  • 重点讲解WebExpressionVoter的初始化。在生成WebExpressionVoter时,需要设置其expressionHandler为 OAuth2WebSecurityExpressionHandler,这样在进行验证时才不会报错。在使用默认的AccessDecisionManager启动进行验证时,Spring Security使用ExpressionUrlAuthorizationConfigurer默认配置WebExpressionVoter,并且在设置expressionHandler为OAuth2WebSecurityExpressionHandler。使用默认配置资源服务器启动时,调试的结果如下:

在资源资源服务器中的详细配置如下:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {/*** 资源服务器内的资源访问控制*/@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/webjars/**", "/v2/api-docs", "/swagger-resources/**", "/swagger-ui.html", "/swagger.json").permitAll().anyRequest().authenticated().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {@Overridepublic <O extends FilterSecurityInterceptor> O postProcess(O fsi) {
// 权限获取自定义配置fsi.setSecurityMetadataSource(new PermissionFilterInvocationSecurityMetadataSource(permissionClient));return fsi;}}).accessDecisionManager(accessDecisionManager());}private AccessDecisionManager accessDecisionManager() {WebExpressionVoter webExpressionVoter = new WebExpressionVoter();webExpressionVoter.setExpressionHandler(new OAuth2WebSecurityExpressionHandler());// 授权逻辑自定义配置return new AffirmativeBased(Lists.newArrayList(new PermissionsVoter(), new RoleVoter(),new AuthenticatedVoter(), webExpressionVoter));}
}

授权测试

在db中配置用户username为admin,password为123456的用户拥有couponDemo的访问权限,在使用postman先认证,然后携带访问coupon/demo api,结果正常返回,操作如图:

在访问时,调用自定义PermissionFilterInvocationSecurityMetadataSource获取配置权限的截图如下:

在进行授权处理时,调用自定义 PermissionsVoter进行授权认证,截图如下:

不足与优化之处

一般的实现中,在每个单独的微服务中配置资源服务器,资源授权成功以后,SecurityContextPersistenceFilter已经把当前登录用户信息存储到SecurityContextHolder上下文,直接根据security提供的SecurityContextHolder.getContext().getAuthentication().getPrincipal()就可以获取当前登录用户信息。如果,微服务不断地增加,一般常见的电商系统都有用户服务,商品服务,订单服务等等,这时,该如何配置资源服务器呢?大家可以思考一下。

推荐:

主流Java进阶技术(学习资料分享)

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!

玩转SpringCloud Security OAuth2资源授权动态权限扩展相关推荐

  1. 十七.SpringCloud+Security+Oauth2实现微服务授权 -非对称加密生成JWT令牌

    仅做学习使用,老鸟飞过,欢迎交流 前言 在之前的微服务授权方案<SpringCloud+Security+Oauth2实现微服务授权 - 授权服务配置>中我们使用的是Oauth+JWT方式 ...

  2. java 接口权限控制_手把手教你搞定权限管理,结合Spring Security实现接口的动态权限控制!...

    SpringBoot实战电商项目mall(30k+star)地址:github.com/macrozheng/- 摘要 权限控管理作为后台管理系统中必要的功能,mall项目中结合Spring Secu ...

  3. 十一.SpringCloud+Security+Oauth2实现微服务授权 - 授权服务配置

    前言 上一文章我们准备了微服务授权的环境,并对AuthServer实现了简单的认证流程,这里是接上一篇文章继续对AuthServer认证服务做Oauth2配置 1.概述Oauth2授权服务配置 我们只 ...

  4. Spring Security Oauth2:授权模式、简单模式 、密码模式 和 客户端模式

    Oauth2的授权模式流程 1.先得到用户的授权grant 2.利用grant得到令牌token 3.根据token获取用户的信息 步骤1:客户端(第三方应用)向用户请求授权. 步骤2:用户单击客户端 ...

  5. SpringBoot + Spring Security Oauth2 客户端授权

    框架使用SpringBoot + Spring Security Oauth2  主要完成了客户端授权  可以通过mysql数据库读取当前客户端表信息进行验证,token存储在数据库中 1.引入依赖 ...

  6. spring security oauth2 常用授权方式配置详细教程(一)

    1 spring security oauth2 简单配置说明(一) 配套源码:https://download.csdn.net/download/tiancxz/12902941 1.1 工程说明 ...

  7. spring Cloud微服务 security+oauth2认证授权中心自定义令牌增强,并实现登录和退出

    文章目录 认证授权中心自定义令牌增强 自定义认证端点返回结果 登录逻辑调整,增强令牌返回参数 测试验证 用户微服务构建 配置类构建 相关实体类 登录 退出登录 在之前的博客我写了 SpringClou ...

  8. Spring Security OAuth2认证授权示例

    本文介绍了如何使用Spring Security OAuth2构建一个授权服务器来验证用户身份以提供access_token,并使用这个access_token来从资源服务器请求数据. 1.概述 OA ...

  9. spring security oauth2 资源服务器配置

    2019独角兽企业重金招聘Python工程师标准>>> import org.springframework.beans.factory.annotation.Autowired; ...

最新文章

  1. JDK 13 新特性一览
  2. 如何释放 DB_RECOVERY_FILE_DEST_SIZE
  3. 邮件服务之Sendmail
  4. 使用redis和fastjson做应用和mysql之间的缓存
  5. PHP 设计模式 笔记与总结(8)策略模式
  6. 最优的cuda线程配置
  7. NonfairSync.tryAcquire
  8. CodeForces - 1031B Curiosity Has No Limits(思维)
  9. 7 centos 源码安装samba_centos 7 安装 samba 服务
  10. 前端学习(2667):退出编辑状态
  11. linux git diff patch,拿到git patch要怎麼用一般patch指令merge?
  12. Serverless实战 —— 使用 Wintersmith + Serverless Framework 快速创建个人站点
  13. 微信小程序插件内页面跳转和参数传递
  14. (4)计数器systemverilog与VHDL编码
  15. 如何在点击事件中取得复选框选中的单元格值
  16. build lavas 失败_Lavas 命令介绍 - Lavas 教程
  17. 参数化CFAR的FPGA实现
  18. 鸿蒙形容欣欣向荣发展,形容发展超迅速的成语
  19. python画人脸代码_10行代码实现python人脸识别
  20. 【自媒体营销神器】一键自动下载短视频并分发至长视频平台脚本开源展示

热门文章

  1. 华为1999元起的智能眼镜,能通话能播放音乐,预售就抢疯了!
  2. 三星Galaxy Fold入网:屏幕故障+数次跳票 热度还有多少?
  3. 产业链加入爆料行列!2019年新iPhone:外形无变化 后置摄像头升级
  4. 美图手机官方正式告别:年中关闭手机业务 手机品牌授权给小米
  5. 这真不是网友P的图?雷军微博曝光小米9 SE真机图 彩虹小米有点炫酷
  6. 苹果AirPods 2预计将于3月25日发布 3月29日正式开卖
  7. 如何取消wlan自动登录 更换wlan帐号
  8. vscode如何添加头部注释、作者注释
  9. flex布局单独一行_CSS3 Flex布局(伸缩布局盒模型)
  10. nodejs 生成证书 和 wss server