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

来源: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. linux 编译opencl,OpenCL编译环境配置(VS+Nvidia)
  2. 目标检测分割--Mask R-CNN
  3. 修改自动生成get/set方法模板代码
  4. Data Mining的十种分析方法
  5. RedHat AS4 配置Yum
  6. php负责传递数据,php传递数据的方法有哪些
  7. 前端学习(1296):第三方模块nodenrm
  8. 数据结构与算法-复杂的问题简单化
  9. jqGrid获取一行数据的方法
  10. 六、Spring MVC之返回数据
  11. java实现网页结构分析,网页列表发现
  12. 什么是STL(模板库)?STL包含的一些内容及基础含义
  13. 【MATLAB】clear和clc用法
  14. win10搭建openvpn以及使用
  15. matlab 山脊 提取,ArcGIS中利用水文分析提取山脊线山谷线
  16. html td 的横向与纵向合并
  17. 对浏览器内核的理解和常见的浏览器内核
  18. 网络工具Netwox和Wireshark详解
  19. 兰芝女王 的炒股心得-转载编辑
  20. java rhino 下载_Java 6的内置Rhino版本和Mozilla直接提供的Rhino包有什么区别?

热门文章

  1. 三星计划在第二代GalaxyFold上采用屏下摄像头技术
  2. 最流畅的手机,性能、跑分却弱爆了?2019年上半年手机数据报告出炉
  3. 真相了!为什么华为不惜成本也要支持外置存储卡扩展?
  4. X战警大战复联要来了?迪士尼收购21世纪福克斯后开始狂秀IP...
  5. 《流浪地球》内地票房超《红海行动》北美上映11天成绩不俗
  6. 图像处理常用八大算法
  7. 我的docker随笔17:使用docker-compose启动MySQL、Redis和Mongo
  8. 我的YUV播放器MFC小笔记:注册表读写
  9. 【maven】mvn deploy 报错 Failed to deploy artifacts: Could not transfer artifact
  10. 【Es】Es 选主流程