一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)(三)

 feimishiwo 2018-08-13

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1


引言: 本文系《認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)》系列的第三篇,本文重點(diǎn)講解token以及API級(jí)別的鑒權(quán)。本文對(duì)涉及到的大部分代碼進(jìn)行了分析,歡迎訂閱本系列文章。


1. 前文回顧

640?wx_fmt=png&wxfrom=5&wx_lazy=1


在開始講解這一篇文章之前,先對(duì)之前兩篇文章進(jìn)行回憶下。在第一篇《認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)(一)》介紹了該項(xiàng)目的背景以及技術(shù)調(diào)研與最后選型。第二篇《認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)(二)》畫出了簡(jiǎn)要的登錄和校驗(yàn)的流程圖,并重點(diǎn)講解了用戶身份的認(rèn)證與token發(fā)放的具體實(shí)現(xiàn)。


640?wx_fmt=png&wxfrom=5&wx_lazy=1


本文重點(diǎn)講解鑒權(quán),包括兩個(gè)方面:token合法性以及API級(jí)別的操作權(quán)限。首先token合法性很容易理解,第二篇文章講解了獲取授權(quán)token的一系列流程,token是否是認(rèn)證服務(wù)器頒發(fā)的,必然是需要驗(yàn)證的。其次對(duì)于API級(jí)別的操作權(quán)限,將上下文信息不具備操作權(quán)限的請(qǐng)求直接拒絕,當(dāng)然此處是設(shè)計(jì)token合法性校驗(yàn)在先,其次再對(duì)操作權(quán)限進(jìn)行驗(yàn)證,如果前一個(gè)驗(yàn)證直接拒絕,通過(guò)則進(jìn)入操作權(quán)限驗(yàn)證。


2.資源服務(wù)器配置

640?wx_fmt=png


ResourceServer配置在第一篇就列出了,在進(jìn)入鑒權(quán)之前,把這邊的配置搞清,即使有些配置在本項(xiàng)目中沒有用到,大家在自己的項(xiàng)目有可能用到。


  1. @Configuration@EnableResourceServerpublic class ResourceServerConfig extends ResourceServerConfigurerAdapter {    //http安全配置
  2.    @Override
  3.    public void configure(HttpSecurity http) throws Exception {        //禁掉csrf,設(shè)置session策略
  4.        http.csrf().disable()
  5.                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  6.                .and()//默認(rèn)允許訪問
  7.                .requestMatchers().antMatchers("/**")
  8.                .and().authorizeRequests()
  9.                .antMatchers("/**").permitAll()
  10.                .anyRequest().authenticated()
  11.                .and().logout() //logout注銷端點(diǎn)配置
  12.                .logoutUrl("/logout")
  13.                .clearAuthentication(true)
  14.                .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())
  15.                .addLogoutHandler(customLogoutHandler());
  16.    }    //添加自定義的CustomLogoutHandler
  17.    @Bean
  18.    public CustomLogoutHandler customLogoutHandler() {        return new CustomLogoutHandler();
  19.    }    //資源安全配置相關(guān)
  20.    @Override
  21.    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {        super.configure(resources);
  22.    }
  23. }


@EnableResourceServer這個(gè)注解很重要,OAuth2資源服務(wù)器的簡(jiǎn)便注解。其使得Spring Security filter通過(guò)請(qǐng)求中的OAuth2 token來(lái)驗(yàn)證請(qǐng)求。通常與EnableWebSecurity配合使用,該注解還創(chuàng)建了硬編碼的@Order(3) WebSecurityConfigurerAdapter,由于當(dāng)前spring的技術(shù),order的順序不易修改,所以在項(xiàng)目中避免還有其他order=3的配置。


關(guān)聯(lián)的HttpSecurity,與之前的 Spring Security XML中的 "http"元素配置類似,它允許配置基于web安全以針對(duì)特定http請(qǐng)求。默認(rèn)是應(yīng)用到所有的請(qǐng)求,通過(guò)requestMatcher可以限定具體URL范圍。HttpSecurity類圖如下。


0?wx_fmt=png


總的來(lái)說(shuō):HttpSecurity是SecurityBuilder接口的一個(gè)實(shí)現(xiàn)類,從名字上我們就可以看出這是一個(gè)HTTP安全相關(guān)的構(gòu)建器。當(dāng)然我們?cè)跇?gòu)建的時(shí)候可能需要一些配置,當(dāng)我們調(diào)用HttpSecurity對(duì)象的方法時(shí),實(shí)際上就是在進(jìn)行配置。


authorizeRequests(),formLogin()、httpBasic()這三個(gè)方法返回的分別是ExpressionUrlAuthorizationConfigurer、FormLoginConfigurer、HttpBasicConfigurer,他們都是SecurityConfigurer接口的實(shí)現(xiàn)類,分別代表的是不同類型的安全配置器。 


因此,從總的流程上來(lái)說(shuō),當(dāng)我們?cè)谶M(jìn)行配置的時(shí)候,需要一個(gè)安全構(gòu)建器SecurityBuilder(例如我們這里的HttpSecurity),SecurityBuilder實(shí)例的創(chuàng)建需要有若干安全配置器SecurityConfigurer實(shí)例的配合。


關(guān)聯(lián)的ResourceServerSecurityConfigurer,為資源服務(wù)器添加特殊的配置,默認(rèn)的適用于很多應(yīng)用,但是這邊的修改至少以resourceId為單位。類圖如下。


0?wx_fmt=png


ResourceServerSecurityConfigurer創(chuàng)建了OAuth2核心過(guò)濾器OAuth2AuthenticationProcessingFilter,并為其提供固定了OAuth2AuthenticationManager。只有被OAuth2AuthenticationProcessingFilter攔截到的oauth2相關(guān)請(qǐng)求才被特殊的身份認(rèn)證器處理。同時(shí)設(shè)置了TokenExtractor、異常處理實(shí)現(xiàn)。


OAuth2AuthenticationProcessingFilter是OAuth2保護(hù)資源的預(yù)先認(rèn)證過(guò)濾器。配合OAuth2AuthenticationManager使用,根據(jù)請(qǐng)求獲取到OAuth2 token,之后就會(huì)使用OAuth2Authentication來(lái)填充Spring Security上下文。 


OAuth2AuthenticationManager在前面的文章給出的AuthenticationManager類圖就出現(xiàn)了,與token認(rèn)證相關(guān)。這邊略過(guò)貼出源碼進(jìn)行講解,讀者可以自行閱讀。


3. 鑒權(quán)endpoint

640?wx_fmt=png


鑒權(quán)主要是使用內(nèi)置的endpoint /oauth/check_token,筆者將對(duì)端點(diǎn)的分析放在前面,因?yàn)檫@是鑒權(quán)的唯一入口。下面我們來(lái)看下該API接口中的主要代碼。


  1.    @RequestMapping(value = "/oauth/check_token")    @ResponseBody
  2.    public Map<String, ?> checkToken(CheckTokenEntity checkTokenEntity) {        //CheckTokenEntity為自定義的dto
  3.        Assert.notNull(checkTokenEntity, "invalid token entity!");        //識(shí)別token
  4.        OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(checkTokenEntity.getToken());        //判斷token是否為空
  5.        if (token == null) {            throw new InvalidTokenException("Token was not recognised");
  6.        }        //未過(guò)期
  7.        if (token.isExpired()) {            throw new InvalidTokenException("Token has expired");
  8.        }        //加載OAuth2Authentication
  9.        OAuth2Authentication authentication = resourceServerTokenServices.loadAuthentication(token.getValue());        //獲取response,token合法性驗(yàn)證完畢
  10.        Map<String, Object> response = (Map<String, Object>) accessTokenConverter.convertAccessToken(token, authentication);        //check for api permission
  11.        if (response.containsKey("jti")) {            //上下文操作權(quán)限校驗(yàn)
  12.            Assert.isTrue(checkPermissions.checkPermission(checkTokenEntity));
  13.        }
  14.        response.put("active", true);    // Always true if token exists and not expired
  15.        return response;
  16.    }


看過(guò)security-oauth源碼的同學(xué)可能立馬就看出上述代碼與源碼不同,熟悉/oauth/check_token校驗(yàn)流程的也會(huì)看出來(lái),這邊筆者對(duì)security-oauth jar進(jìn)行了重新編譯,修改了部分源碼用于該項(xiàng)目需求的場(chǎng)景。主要是加入了前置的API級(jí)別的權(quán)限校驗(yàn)。


4. token 合法性驗(yàn)證

640?wx_fmt=png


從上面的CheckTokenEndpoint中可以看出,對(duì)于token合法性驗(yàn)證首先是識(shí)別請(qǐng)求體中的token。用到的主要方法是ResourceServerTokenServices提供的readAccessToken()方法。該接口的實(shí)現(xiàn)類為DefaultTokenServices,在之前的配置中有講過(guò)這邊配置了jdbc的TokenStore。


  1. public class JdbcTokenStore implements TokenStore {
  2.    ...    public OAuth2AccessToken readAccessToken(String tokenValue) {
  3.        OAuth2AccessToken accessToken = null;        try {            //使用selectAccessTokenSql語(yǔ)句,調(diào)用了私有的extractTokenKey()方法
  4.            accessToken = jdbcTemplate.queryForObject(selectAccessTokenSql, new RowMapper<OAuth2AccessToken>() {                public OAuth2AccessToken mapRow(ResultSet rs, int rowNum) throws SQLException {                    return deserializeAccessToken(rs.getBytes(2));
  5.                }
  6.            }, extractTokenKey(tokenValue));
  7.        }        //異常情況
  8.        catch (EmptyResultDataAccessException e) {            if (LOG.isInfoEnabled()) {
  9.                LOG.info("Failed to find access token for token " + tokenValue);
  10.            }
  11.        }        catch (IllegalArgumentException e) {
  12.            LOG.warn("Failed to deserialize access token for " + tokenValue, e);            //不合法則移除
  13.            removeAccessToken(tokenValue);
  14.        }        return accessToken;
  15.    }
  16.    ...    //提取TokenKey方法
  17.    protected String extractTokenKey(String value) {        if (value == null) {            return null;
  18.        }
  19.        MessageDigest digest;        try {            //MD5
  20.            digest = MessageDigest.getInstance("MD5");
  21.        }        catch (NoSuchAlgorithmException e) {            throw new IllegalStateException("MD5 algorithm not available.  Fatal (should be in the JDK).");
  22.        }        try {            byte[] bytes = digest.digest(value.getBytes("UTF-8"));            return String.format("%032x", new BigInteger(1, bytes));
  23.        }        catch (UnsupportedEncodingException e) {            throw new IllegalStateException("UTF-8 encoding not available.  Fatal (should be in the JDK).");
  24.        }
  25.    }
  26. }


readAccessToken()檢索出該token值的完整信息。上述代碼比較簡(jiǎn)單,涉及到的邏輯也不復(fù)雜,此處簡(jiǎn)單講解。下圖為debug token校驗(yàn)的變量信息,讀者可以自己動(dòng)手操作下,截圖僅供參考。


0?wx_fmt=png


至于后面的步驟,loadAuthentication()為特定的access token 加載credentials。得到的credentials 與token作為convertAccessToken()參數(shù),得到校驗(yàn)token的response。


5. API級(jí)別權(quán)限校驗(yàn)

640?wx_fmt=png


筆者項(xiàng)目目前都是基于Web的權(quán)限驗(yàn)證,之前遺留的一個(gè)巨大的單體應(yīng)用系統(tǒng)正在逐漸拆分,然而當(dāng)前又不能完全拆分完善。為了同時(shí)兼容新舊服務(wù),盡量減少對(duì)業(yè)務(wù)系統(tǒng)的入侵,實(shí)現(xiàn)微服務(wù)的統(tǒng)一性和獨(dú)立性。筆者根據(jù)業(yè)務(wù)業(yè)務(wù)場(chǎng)景,嘗試在Auth處做操作權(quán)限校驗(yàn)。


首先想到的是資源服務(wù)器配置ResourceServer,如:


http.authorizeRequests()
.antMatchers("/order/**").access("#oauth2.hasScope('select') and hasRole('ROLE_USER')")


這樣做需要將每個(gè)操作接口的API權(quán)限控制放在各個(gè)不同的業(yè)務(wù)服務(wù),每個(gè)服務(wù)在接收到請(qǐng)求后,需要先從Auth服務(wù)取出該token 對(duì)應(yīng)的role和scope等權(quán)限信息。這個(gè)方法肯定是可行的,但是由于項(xiàng)目鑒權(quán)的粒度更細(xì),而且暫時(shí)不想大動(dòng)原有系統(tǒng),在加上之前網(wǎng)關(guān)設(shè)計(jì),網(wǎng)關(guān)調(diào)用Auth服務(wù)校驗(yàn)token合法性,所以最后決定在Auth系統(tǒng)調(diào)用中,把這些校驗(yàn)一起解決完。


文章開頭資源服務(wù)器的配置代碼可以看出,對(duì)于所有的資源并沒有做攔截,因?yàn)榫W(wǎng)關(guān)處是調(diào)用Auth系統(tǒng)的相關(guān)endpoint,并不是所有的請(qǐng)求url都會(huì)經(jīng)過(guò)一遍Auth系統(tǒng),所以對(duì)于所有的資源,在Auth系統(tǒng)中,定義需要鑒權(quán)接口所需要的API權(quán)限,然后根據(jù)上下文進(jìn)行匹配。這是采用的第二種方式,也是筆者目前采用的方法。當(dāng)然這種方式的弊端也很明顯,一旦并發(fā)量大,網(wǎng)關(guān)還要耗時(shí)在調(diào)用Auth系統(tǒng)的鑒權(quán)上,TPS勢(shì)必要下降很多,對(duì)于一些不需要鑒權(quán)的服務(wù)接口也會(huì)引起不可用。另外一點(diǎn)是,對(duì)于某些特殊權(quán)限的接口,需要的上下文信息很多,可能并不能完全覆蓋,對(duì)于此,筆者的解決是分兩方面:一是盡量將這些特殊情況進(jìn)行分類,某一類的情況統(tǒng)一解決;二是將嚴(yán)苛的校驗(yàn)降低,對(duì)于上下文校驗(yàn)失敗的直接拒絕,而通過(guò)的,對(duì)于某些接口,在接口內(nèi)進(jìn)行操作之前,對(duì)特殊的地方還要再次進(jìn)行校驗(yàn)。


上面在講endpoint有提到這邊對(duì)源碼進(jìn)行了改寫。CheckTokenEntity是自定義的DTO,這這個(gè)類中定義了鑒權(quán)需要的上下文,這里是指能校驗(yàn)操作權(quán)限的最小集合,如URI、roleId、affairId等等。另外定義了CheckPermissions接口,其方法checkPermission(CheckTokenEntity checkTokenEntity)返回了check的結(jié)果。而其具體實(shí)現(xiàn)類則定義在Auth系統(tǒng)中。筆者項(xiàng)目中調(diào)用的實(shí)例如下:


  1. @Componentpublic class CustomCheckPermission implements CheckPermissions {    @Autowired
  2.    private PermissionService permissionService;    @Override
  3.    public boolean checkPermission(CheckTokenEntity checkTokenEntity) {
  4.        String url = checkTokenEntity.getUri();
  5.        Long affairId = checkTokenEntity.getAffairId();
  6.        Long roleId = checkTokenEntity.getRoleId();        //校驗(yàn)
  7.        if (StringUtils.isEmpty(url) || affairId <= 0 || roleId <= 0) {            return true;
  8.        } else {            return permissionService.checkPermission(url, affairId, roleId);
  9.        }
  10.    }
  11. }


關(guān)于jar包spring-cloud-starter-oauth2中的具體修改內(nèi)容,大家可以看下文末筆者的GitHub項(xiàng)目。通過(guò)自定義CustomCheckPermission,覆寫checkPermission()方法,大家也可以對(duì)自己業(yè)務(wù)的操作權(quán)限進(jìn)行校驗(yàn),非常靈活。這邊涉及到具體業(yè)務(wù),筆者在項(xiàng)目中只提供接口,具體的實(shí)現(xiàn)需要讀者自行完成。


6. 總結(jié)

640?wx_fmt=png


本文相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,主要講解了token以及API級(jí)別的鑒權(quán)。token的合法性認(rèn)證很常規(guī),Auth系統(tǒng)對(duì)于API級(jí)別的鑒權(quán)是結(jié)合自身業(yè)務(wù)需要和現(xiàn)狀進(jìn)行的設(shè)計(jì)。這兩塊的校驗(yàn)都前置到Auth系統(tǒng)中,優(yōu)缺點(diǎn)在上面的小節(jié)也有講述。最后,架構(gòu)設(shè)計(jì)根據(jù)自己的需求和現(xiàn)狀,筆者的解決思路僅供參考。


本文的源碼地址:


  • GitHub:https://github.com/keets2012/Auth-service 

  • 碼云: https:///keets/Auth-Service


參考


  • 微服務(wù)API級(jí)權(quán)限的技術(shù)架構(gòu)

    (http://blog.csdn.net/OmniStack/article/details/77881185)

  • spring-security-oauth

    (http://projects./spring-security-oauth/docs/oauth2.html)

  • Spring-Security Docs

    (http://projects./spring-security/)


原文鏈接:http:///2017/10/24/security3/


深入學(xué)習(xí)Kubernetes

640?wx_fmt=png


本次培訓(xùn)內(nèi)容包含:Kubernetes架構(gòu)、Kubernetes安裝、Kubernetes功能導(dǎo)覽、監(jiān)控解決方案、Kubernetes高階——設(shè)計(jì)和實(shí)現(xiàn)、Kubernetes落地實(shí)踐等,點(diǎn)擊識(shí)別下方二維碼加微信好友了解具體培訓(xùn)內(nèi)容



點(diǎn)擊閱讀原文鏈接即可報(bào)名。 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    中文字幕中文字幕在线十八区| 亚洲超碰成人天堂涩涩| 精品久久久一区二区三| 三级高清有码在线观看| 99少妇偷拍视频在线| 国产免费成人激情视频| 国产综合香蕉五月婷在线| 国产亚洲精品香蕉视频播放| 欧美人禽色视频免费看| 人妻乱近亲奸中文字幕| av一区二区三区天堂| 欧美自拍偷自拍亚洲精品| 黄色片一区二区在线观看| 国产精品久久三级精品| 亚洲中文字幕视频在线观看| 青青操成人免费在线视频| 国产成人精品一区二区在线看| 欧美日韩少妇精品专区性色| 一区二区日韩欧美精品| 伊人欧美一区二区三区| 国产高清在线不卡一区| 色婷婷人妻av毛片一区二区三区| 国产原创中文av在线播放| 欧美一级片日韩一级片| 91亚洲国产成人久久精品麻豆| 国产亚洲中文日韩欧美综合网| 欧美不卡高清一区二区三区| 国产韩国日本精品视频| 国产又粗又猛又爽又黄的文字| 丝袜视频日本成人午夜视频 | 精产国品一二三区麻豆| 国产精品流白浆无遮挡| 黄色日韩欧美在线观看| 日韩一区二区免费在线观看| 色婷婷视频免费在线观看| 国产又大又黄又粗又免费| 亚洲中文字幕剧情在线播放| 美女激情免费在线观看| 九九热视频经典在线观看| 亚洲欧美日韩综合在线成成| 免费精品一区二区三区|