<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          玩轉(zhuǎn)SpringCloud Security OAuth2資源授權(quán)動(dòng)態(tài)權(quán)限擴(kuò)展

          共 3745字,需瀏覽 8分鐘

           ·

          2021-11-26 23:42

          點(diǎn)擊關(guān)注公眾號,Java干貨及時(shí)送達(dá)??

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

          details/104731154

          在Spring Cloud Security 中,認(rèn)證和授權(quán)都是通過FilterChainProxy(Servlet Filter過濾器)攔截然后進(jìn)行操作的。FilterSecurityInterceptor過濾器攔截了受保護(hù)資源的請求,然后進(jìn)行授權(quán)處理,授權(quán)驗(yàn)證的邏輯在其父類AbstractSecurityInterceptor實(shí)現(xiàn)。大致流程如下:

          • 使用SecurityMetadataSource根據(jù)http請求獲取對應(yīng)擁有的權(quán)限。
          • 使用Spring Security授權(quán)模塊對用戶訪問的資源進(jìn)行授權(quán)驗(yàn)證。

          AbstractSecurityInterceptor的部分源碼如下:

          ????//?AbstractSecurityInterceptor.java
          ?protected?InterceptorStatusToken?beforeInvocation(Object?object)?{
          ????????......
          ?
          ????????//?根據(jù)http請求獲取對應(yīng)的配置的權(quán)限信息
          ??Collection?attributes?=?this.obtainSecurityMetadataSource()
          ????.getAttributes(object);
          ?
          ?????......
          ????????//?對用戶認(rèn)證進(jìn)行校驗(yàn)
          ??Authentication?authenticated?=?authenticateIfRequired();
          ??try?{
          ????????????//?對用戶的權(quán)限與訪問資源擁有的權(quán)限進(jìn)行校驗(yàn)
          ???this.accessDecisionManager.decide(authenticated,?object,?attributes);
          ??}
          ??catch?(AccessDeniedException?accessDeniedException)?{
          ???publishEvent(new?AuthorizationFailureEvent(object,?attributes,?authenticated,
          ?????accessDeniedException));
          ?
          ???throw?accessDeniedException;
          ??}
          ????????......
          ?}

          在默認(rèn)的SecurityMetadataSource的子類DefaultFilterInvocationSecurityMetadataSource實(shí)現(xiàn)中,會把資源服務(wù)器配置的權(quán)限信息全部加載到內(nèi)存。如果要實(shí)現(xiàn)授權(quán)權(quán)限的動(dòng)態(tài)修改,需要擴(kuò)展SecurityMetadataSource,例如,使權(quán)限數(shù)據(jù)能夠動(dòng)態(tài)的從數(shù)據(jù)庫獲取。并且,自定義根據(jù)動(dòng)態(tài)權(quán)限認(rèn)證邏輯AccessDecisionVoter。

          擴(kuò)展SecurityMetadataSource

          自定義PermissionFilterInvocationSecurityMetadataSource,參考默認(rèn)的DefaultFilterInvocationSecurityMetadataSource實(shí)現(xiàn)從數(shù)據(jù)庫動(dòng)態(tài)的根據(jù)訪問http請求獲取配置的權(quán)限。由于每次都需要獲取全部的有效的權(quán)限配置數(shù)據(jù),可以對權(quán)限數(shù)據(jù)做一個(gè)本地緩存,提交查詢效率。

          在ConfigAttribute的子類實(shí)現(xiàn)中,可以使用SecurityConfig保存配置的權(quán)限. 訪問規(guī)則ConfigAttribute。實(shí)現(xiàn)代碼如下:

          public?class?PermissionFilterInvocationSecurityMetadataSource?implements?FilterInvocationSecurityMetadataSource?{
          ?
          ????private?final?PermissionClient?permissionClient;
          ?
          ????public?PermissionFilterInvocationSecurityMetadataSource(PermissionClient?permissionClient)?{
          ????????this.permissionClient?=?permissionClient;
          ????}
          ?
          ????/**
          ?????*?轉(zhuǎn)換權(quán)限列表
          ?????*/

          ????private?Map>?requestMatcherCollectionMap()?{
          ?
          ????????List?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()))));
          ????}
          ?
          ?
          ????@Override
          ????public?Collection?getAttributes(Object?object)?throws?IllegalArgumentException?{
          ?
          ????????final?HttpServletRequest?request?=?((FilterInvocation)?object).getRequest();
          ????????for?(Map.Entry>?entry?:?requestMatcherCollectionMap().entrySet())?{
          ????????????if?(entry.getKey().matches(request))?{
          ????????????????return?entry.getValue();
          ????????????}
          ????????}
          ????????return?null;
          ????}
          ?
          ????@Override
          ????public?Collection?getAllConfigAttributes()?{
          ????????return?requestMatcherCollectionMap().values()
          ????????????????.stream().flatMap(Collection::stream)
          ????????????????.collect(Collectors.toList());
          ????}
          ?
          ????@Override
          ????public?boolean?supports(Class?clazz)?{
          ????????return?FilterInvocation.class.isAssignableFrom(clazz);
          ????}
          }

          擴(kuò)展根據(jù)權(quán)限授權(quán)邏輯

          自定義PermissionsVoter類實(shí)現(xiàn)AccessDecisionVoter接口,實(shí)現(xiàn)了用戶只要擁有訪問資源的權(quán)限就可以訪問。參考RoleVoter具體的實(shí)現(xiàn)邏輯,代碼如下:

          public?class?PermissionsVoter?implements?AccessDecisionVoter<Object>?{
          ?
          ????@Override
          ????public?boolean?supports(ConfigAttribute?attribute)?{
          ????????return?Objects.nonNull(attribute.getAttribute());
          ????}
          ?
          ????@Override
          ????public?boolean?supports(Class?clazz)?{
          ????????return?true;
          ????}
          ?
          ????@Override
          ????public?int?vote(Authentication?authentication,?Object?object,?Collection?attributes)?{
          ?
          ????????if?(CollectionUtils.isEmpty(attributes))?{
          ????????????return?ACCESS_DENIED;
          ????????}
          ????????//?用戶授權(quán)的權(quán)限
          ????????Collection?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?attributes)?{
          ?
          ????????for?(ConfigAttribute?configAttribute?:?attributes)?{
          ????????????String?attribute;
          ????????????if?(StringUtils.isNotBlank(attribute?=?configAttribute.getAttribute())
          ????????????????????&&?attribute.equals(authority))?{
          ????????????????return?true;
          ????????????}
          ????????}
          ????????return?false;
          ????}
          ?
          ????/**
          ?????*?獲取用戶權(quán)限列表
          ?????*/

          ????Collection?extractAuthorities(Authentication?authentication)?{
          ????????return?authentication.getAuthorities();
          ????}
          }

          配置資源服務(wù)器

          在配置資源服務(wù)器,主要是如下配置:

          • SecurityMetadataSource,獲取資源權(quán)限的設(shè)置
          • AccessDecisionManager,自定義授權(quán)邏輯的配置

          重點(diǎn)講解下自定義AccessDecisionManager的情況,

          • 選擇AffirmativeBased(只要有一個(gè)授權(quán)處理通過則可以進(jìn)行訪問)。
          • 配置RoleVoter(角色授權(quán)),AuthenticatedVoter(認(rèn)證信息授權(quán)), WebExpressionVoter(EL描述授權(quán))spring security默認(rèn)的授權(quán)邏輯。
          • 重點(diǎn)講解WebExpressionVoter的初始化。在生成WebExpressionVoter時(shí),需要設(shè)置其expressionHandler為 OAuth2WebSecurityExpressionHandler,這樣在進(jìn)行驗(yàn)證時(shí)才不會報(bào)錯(cuò)。在使用默認(rèn)的AccessDecisionManager啟動(dòng)進(jìn)行驗(yàn)證時(shí),Spring Security使用ExpressionUrlAuthorizationConfigurer默認(rèn)配置WebExpressionVoter,并且在設(shè)置expressionHandler為OAuth2WebSecurityExpressionHandler。使用默認(rèn)配置資源服務(wù)器啟動(dòng)時(shí),調(diào)試的結(jié)果如下:

          在資源資源服務(wù)器中的詳細(xì)配置如下:

          @Configuration
          @EnableResourceServer
          public?class?ResourceServerConfig?extends?ResourceServerConfigurerAdapter?{
          ?
          ?
          ????/**
          ?????*?資源服務(wù)器內(nèi)的資源訪問控制
          ?????*/

          ????@Override
          ????public?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()?{
          ?
          ????????????????????@Override
          ????????????????????public??O?postProcess(O?fsi)?{
          //?權(quán)限獲取自定義配置
          ????????????????????????fsi.setSecurityMetadataSource(new?PermissionFilterInvocationSecurityMetadataSource(permissionClient));
          ????????????????????????return?fsi;
          ????????????????????}
          ????????????????})
          ????????????????.accessDecisionManager(accessDecisionManager());
          ????}
          ?
          ????private?AccessDecisionManager?accessDecisionManager()?{
          ?
          ????????WebExpressionVoter?webExpressionVoter?=?new?WebExpressionVoter();
          ????????webExpressionVoter.setExpressionHandler(new?OAuth2WebSecurityExpressionHandler());
          ????????//?授權(quán)邏輯自定義配置
          ????????return?new?AffirmativeBased(Lists.newArrayList(new?PermissionsVoter(),?new?RoleVoter(),
          ????????????????new?AuthenticatedVoter(),?webExpressionVoter));
          ????}
          }

          授權(quán)測試

          在db中配置用戶username為admin,password為123456的用戶擁有couponDemo的訪問權(quán)限,在使用postman先認(rèn)證,然后攜帶訪問coupon/demo api,結(jié)果正常返回,操作如圖:

          在訪問時(shí),調(diào)用自定義PermissionFilterInvocationSecurityMetadataSource獲取配置權(quán)限的截圖如下:

          在進(jìn)行授權(quán)處理時(shí),調(diào)用自定義 PermissionsVoter進(jìn)行授權(quán)認(rèn)證,截圖如下:

          不足與優(yōu)化之處

          一般的實(shí)現(xiàn)中,在每個(gè)單獨(dú)的微服務(wù)中配置資源服務(wù)器,資源授權(quán)成功以后,SecurityContextPersistenceFilter已經(jīng)把當(dāng)前登錄用戶信息存儲到SecurityContextHolder上下文,直接根據(jù)security提供的SecurityContextHolder.getContext().getAuthentication().getPrincipal()就可以獲取當(dāng)前登錄用戶信息。如果,微服務(wù)不斷地增加,一般常見的電商系統(tǒng)都有用戶服務(wù),商品服務(wù),訂單服務(wù)等等,這時(shí),該如何配置資源服務(wù)器呢?大家可以思考一下。

          1.?Java中clone( )和new效率哪個(gè)更高?

          2.?Linux 打包及壓縮命令使用方法總結(jié)

          3.?千萬不要這樣使用 @Async 注解 !

          4.?手把手10分鐘實(shí)現(xiàn) Spring Boot 發(fā)送郵件功能

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號并回復(fù)?Java?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 60
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美三级电影在线播放 | 免费看成人747474九号视频在线观看 | 国产日批视频免费观看 | 中文一区二区 | 国产18第一无限资源网站 |