<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 前言 今天來聊聊一個接口對接的場景,A廠家有一套HTTP接口需要提供給B廠家使用,由于是外網環境,所以需要有一套安全機制保障,這個時候oauth2就可以作為一個方案。 關于oauth2,其實是一個規范,本文重點講解spring對他進行的實現。 需要對spring security有一定的配置使用經驗,用戶認證這一塊,spring security oauth2建立在spring security的基礎之上。第一篇文章主要是講解使用springboot搭建一個簡易的授權,資源服務器。后續文章會進行spring security oauth2的相關源碼分析。Java中的安全框架如shrio,已經有跟我學shiro - 開濤,非常成體系地,深入淺出地講解了apache的這個開源安全框架,但是spring security包括oauth2一直沒有成體系的文章,學習它們大多依賴于較少的官方文檔,理解一下基本的使用配置;通過零散的博客,了解一下他人的使用經驗;打斷點,分析內部的工作流程;看源碼中的接口設計,以及注釋,了解設計者的用意。spring的各個框架都運用了很多的設計模式,在學習源碼的過程中,也大概了解了一些套路。spring也在必要的地方添加了適當的注釋,避免了源碼閱讀者對于一些細節設計的理解產生偏差,讓我更加感嘆,spring不僅僅是一個工具框架,更像是一個藝術品。 ## 概述 使用oauth2保護你的應用,可以分為簡易的分為三個步驟 * 配置資源服務器 * 配置認證服務器 * 配置spring security 前兩點是oauth2的主體內容,但前面我已經描述過了,spring security oauth2是建立在spring security基礎之上的,所以有一些體系是公用的。 oauth2根據使用場景不同,分成了4種模式 * 授權碼模式(authorization code) * 簡化模式(implicit) * 密碼模式(resource owner password credentials) * 客戶端模式(client credentials) 本文重點講解接口對接中常使用的密碼模式(以下簡稱password模式)和客戶端模式(以下簡稱client模式)。授權碼模式使用到了回調地址,是最為復雜的方式,通常網站中經常出現的微博,qq第三方登錄,都會采用這個形式。簡化模式不常用。 ## 項目準備 主要的maven依賴如下 ~~~ <!-- 注意是starter,自動配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- 不是starter,手動配置 --> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 將token存儲在redis中 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ~~~ 我們給自己先定個目標,要干什么事?既然說到保護應用,那必須得先有一些資源,我們創建一個endpoint作為提供給外部的接口: ~~~ @RestController public class TestEndpoints { @GetMapping("/product/{id}") public String getProduct(@PathVariable String id) { //for debug Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return "product id : " + id; } @GetMapping("/order/{id}") public String getOrder(@PathVariable String id) { //for debug Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return "order id : " + id; } } ~~~ 暴露一個商品查詢接口,后續不做安全限制,一個訂單查詢接口,后續添加訪問控制。 ## 配置資源服務器和授權服務器 由于是兩個oauth2的核心配置,我們放到一個配置類中。 為了方便下載代碼直接運行,我這里將客戶端信息放到了內存中,生產中可以配置到數據庫中。token的存儲一般選擇使用Redis,一是性能比較好,二是自動過期的機制,符合token的特性。 ~~~ @Configuration public class OAuth2ServerConfig { private static final String DEMO_RESOURCE_ID = "order"; @Configuration @EnableResourceServer protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Override public void configure(ResourceServerSecurityConfigurer resources) { resources.resourceId(DEMO_RESOURCE_ID).stateless(true); } @Override public void configure(HttpSecurity http) throws Exception { // @formatter:off http // Since we want the protected resources to be accessible in the UI as well we need // session creation to be allowed (it's disabled by default in 2.0.6) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and() .requestMatchers().anyRequest() .and() .anonymous() .and() .authorizeRequests() // .antMatchers("/product/**").access("#oauth2.hasScope('select') and hasRole('ROLE_USER')") .antMatchers("/order/**").authenticated();//配置order訪問控制,必須認證過后才可以訪問 // @formatter:on } } @Configuration @EnableAuthorizationServer protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Autowired AuthenticationManager authenticationManager; @Autowired RedisConnectionFactory redisConnectionFactory; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { //配置兩個客戶端,一個用于password認證一個用于client認證 clients.inMemory().withClient("client_1") .resourceIds(DEMO_RESOURCE_ID) .authorizedGrantTypes("client_credentials", "refresh_token") .scopes("select") .authorities("client") .secret("123456") .and().withClient("client_2") .resourceIds(DEMO_RESOURCE_ID) .authorizedGrantTypes("password", "refresh_token") .scopes("select") .authorities("client") .secret("123456"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .tokenStore(new RedisTokenStore(redisConnectionFactory)) .authenticationManager(authenticationManager); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { //允許表單認證 oauthServer.allowFormAuthenticationForClients(); } } } ~~~ 簡單說下spring security oauth2的認證思路。 * client模式,沒有用戶的概念,直接與認證服務器交互,用配置中的客戶端信息去申請accessToken,客戶端有自己的client_id,client_secret對應于用戶的username,password,而客戶端也擁有自己的authorities,當采取client模式認證時,對應的權限也就是客戶端自己的authorities。 * password模式,自己本身有一套用戶體系,在認證時需要帶上自己的用戶名和密碼,以及客戶端的client_id,client_secret。此時,accessToken所包含的權限是用戶本身的權限,而不是客戶端的權限。 我對于兩種模式的理解便是,如果你的系統已經有了一套用戶體系,每個用戶也有了一定的權限,可以采用password模式;如果僅僅是接口的對接,不考慮用戶,則可以使用client模式。 ## 配置spring security 在spring security的版本迭代中,產生了多種配置方式,建造者模式,適配器模式等等設計模式的使用,spring security內部的認證flow也是錯綜復雜,在我一開始學習ss也產生了不少困惑,總結了一下配置經驗:使用了springboot之后,spring security其實是有不少自動配置的,我們可以僅僅修改自己需要的那一部分,并且遵循一個原則,直接覆蓋最需要的那一部分。這一說法比較抽象,舉個例子。比如配置內存中的用戶認證器。有兩種配置方式 planA: ~~~ @Bean protected UserDetailsService userDetailsService(){ InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("user_1").password("123456").authorities("USER").build()); manager.createUser(User.withUsername("user_2").password("123456").authorities("USER").build()); return manager; } ~~~ planB: ~~~ @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("user_1").password("123456").authorities("USER") .and() .withUser("user_2").password("123456").authorities("USER"); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { AuthenticationManager manager = super.authenticationManagerBean(); return manager; } } ~~~ 你最終都能得到配置在內存中的兩個用戶,前者是直接替換掉了容器中的UserDetailsService,這么做比較直觀;后者是替換了AuthenticationManager,當然你還會在SecurityConfiguration 復寫其他配置,這么配置最終會由一個委托者去認證。如果你熟悉spring security,會知道AuthenticationManager和AuthenticationProvider以及UserDetailsService的關系,他們都是頂級的接口,實現類之間錯綜復雜的聚合關系…配置方式千差萬別,但理解清楚認證流程,知道各個實現類對應的職責才是掌握spring security的關鍵。 下面給出我最終的配置: ~~~ @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Bean @Override protected UserDetailsService userDetailsService(){ InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("user_1").password("123456").authorities("USER").build()); manager.createUser(User.withUsername("user_2").password("123456").authorities("USER").build()); return manager; } @Override protected void configure(HttpSecurity http) throws Exception { // @formatter:off http .requestMatchers().anyRequest() .and() .authorizeRequests() .antMatchers("/oauth/*").permitAll(); // @formatter:on } } ~~~ 重點就是配置了一個UserDetailsService,和ClientDetailsService一樣,為了方便運行,使用內存中的用戶,實際項目中,一般使用的是數據庫保存用戶,具體的實現類可以使用JdbcDaoImpl或者JdbcUserDetailsManager。 ## 獲取token 進行如上配置之后,啟動springboot應用就可以發現多了一些自動創建的endpoints: ~~~ {[/oauth/authorize]} {[/oauth/authorize],methods=[POST] {[/oauth/token],methods=[GET]} {[/oauth/token],methods=[POST]} {[/oauth/check_token]} {[/oauth/error]} ~~~ 重點關注一下/oauth/token,它是獲取的token的endpoint。啟動springboot應用之后,使用http工具訪問 : * password模式:http://localhost:8080/oauth/token? username=user_1&password=123456& grant_type=password&scope=select& client_id=client_2&client_secret=123456,響應如下: ~~~ { "access_token":"950a7cc9-5a8a-42c9-a693-40e817b1a4b0", "token_type":"bearer", "refresh_token":"773a0fcd-6023-45f8-8848-e141296cb3cb", "expires_in":27036, "scope":"select" } ~~~ * client模式:http://localhost:8080/oauth/token? grant_type=client_credentials& scope=select& client_id=client_1& client_secret=123456,響應如下: ~~~ { "access_token":"56465b41-429d-436c-ad8d-613d476ff322", "token_type":"bearer", "expires_in":25074, "scope":"select" } ~~~ 在配置中,我們已經配置了對order資源的保護,如果直接訪問: http://localhost:8080/order/1 ,會得到這樣的響應: ~~~ { "error":"unauthorized", "error_description":"Full authentication is required to access this resource" } ~~~ (這樣的錯誤響應可以通過重寫配置來修改) 而對于未受保護的product資源 `http://localhost:8080/product/1` 則可以直接訪問,得到響應 `product id : 1` 攜帶accessToken參數訪問受保護的資源: 使用password模式獲得的token: `http://localhost:8080/order/1?access_token=950a7cc9-5a8a-42c9-a693-40e817b1a4b0` 得到了之前匿名訪問無法獲取的資源: `order id : 1` 使用client模式獲得的token: `http://localhost:8080/order/1?access_token=56465b41-429d-436c-ad8d-613d476ff322` 同上的響應 `order id : 1` ## 總結 到這兒,一個簡單的oauth2入門示例就完成了,一個簡單的配置教程。token的工作原理是什么,它包含了哪些信息?spring內部如何對身份信息進行驗證?以及上述的配置到底影響了什么?這些內容會放到后面的文章中去分析。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看