Spring cloud系列十九 Spring Cloud G版本集成oAuth2.0

背景

因爲項目的關係,系統須要集成oAuth2.0功能。關於OAuth2.0的概念參考文章理解OAuth 2.0,或者 OAuth 2.0最簡嚮導html

實現自定義功能

依賴jar

咱們系統使用的Spring cloud G版本,引入依賴jar包redis

<!-- oAuth2 -->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-oauth2</artifactId>
  </dependency>
複製代碼

OAuth2.0 代碼概述

OAuth 2.0 定義了四種受權方式spring

  • ○ 受權碼模式(authorization code)
  • ○ 簡化模式(implicit)
  • ○ 密碼模式(resource owner password credentials)
  • ○ 客戶端模式(client credentials)

在Spring OAuth2.0中除了定義以上四種受權模式外,還定義一個特殊的受權模式:刷新 四種模式和refresh模式對應的處理類是:數據庫

  • ○ AuthorizationCodeTokenGranter: 對應 authorization_code 模式
  • ○ ImplicitTokenGranter :對應 implicit模式
  • ○ ClientCredentialsTokenGranter:對應 client_credentials模式
  • ○ ResourceOwnerPasswordTokenGranter:對應 password 模式
  • ○ RefreshTokenGranter: 對應 refresh_token模式

其餘一些重要的類:緩存

  • ○ AuthorizationCodeServices:用於「受權碼模式( authorization code )」如何保存"受權碼",默認有兩種實現:bash

    • § InMemoryAuthorizationCodeServices:受權碼保存在內存中
    • § JdbcAuthorizationCodeServices:受權碼保存在數據庫中
  • ○ TokenStore:存儲token的方式,經常使用有如下幾種實現服務器

    • § InMemoryTokenStore:內存存儲
    • § JdbcTokenStore:數據庫存儲
    • § RedisTokenStore:redis存儲
    • § JwkTokenStore和JwtTokenStore
  • ○ ClientDetailsService:客戶端帳號管理app

    • § InMemoryClientDetailsService:使用內存管理客戶端帳號
    • § JdbcClientDetailsService:使用數據庫管理客戶端帳號
  • ○ UserDetailsService:如何加載用戶帳號curl

    • § InMemoryUserDetailsManager:從內存中加載用戶帳號
    • § JdbcUserDetailsManager:從數據庫中加載用戶帳號
  • ○ 其餘ide

    • § DefaultTokenServices:token服務類,token的處理邏輯很大部分在這裏

實現自定義客戶端帳戶(不是用戶的帳戶)

  • ○ ClientDetailsService:客戶端帳號管理
    • § InMemoryClientDetailsService:使用內存管理客戶端帳號
    • § JdbcClientDetailsService:使用數據庫管理客戶端帳號

網上的案列基本是使用JdbcClientDetailsService 和 InMemoryClientDetailsService 實現客戶端帳號。這些都不符合咱們的要求。JdbcClientDetailsService 必須按照的標準創建相關的數據庫表,咱們的已經有本身的帳戶表,不能使用這些建立的表。

咱們首先研究InMemoryClientDetailsService裏返回的ClientDetails實例值以下:

在這裏插入圖片描述
咱們參考JdbcClientDetailsService 和 InMemoryClientDetailsService代碼,實現自定義ClientDetailsService。關鍵代碼以下

@Service
public class MyClientDetailsService implements ClientDetailsService {
    @Override
    public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
        System.out.println("clientId = " + clientId);
        // 實際使用時,這裏增長讀數據庫的操做,根據clientId獲取帳戶信息,並根據下面代碼的方式生成BaseClientDetails對象便可
        
BaseClientDetails  baseClientDetails = new BaseClientDetails();
        baseClientDetails.setClientId("clientapp");
        // secret密碼配置從 Spring Security 5.0開始必須以 {加密方式}+加密後的密碼 這種格式填寫
        /*
         *   當前版本5新增支持加密方式:
         *   bcrypt - BCryptPasswordEncoder (Also used for encoding)
         *   ldap - LdapShaPasswordEncoder
         *   MD4 - Md4PasswordEncoder
         *   MD5 - new MessageDigestPasswordEncoder("MD5")
         *   noop - NoOpPasswordEncoder
         *   pbkdf2 - Pbkdf2PasswordEncoder
         *   scrypt - SCryptPasswordEncoder
         *   SHA-1 - new MessageDigestPasswordEncoder("SHA-1")
         *   SHA-256 - new MessageDigestPasswordEncoder("SHA-256")
         *   sha256 - StandardPasswordEncoder
         */
        baseClientDetails.setClientSecret("{noop}112233");

        // 不能夠設置爲null
       // baseClientDetails.setAutoApproveScopes(null);

        List<String> scopeList = new ArrayList<>();
        scopeList.add("read_userinfo");
        scopeList.add("read_contacts");
        baseClientDetails.setScope(scopeList);

        baseClientDetails.setResourceIds(null);

        List<String> grantTypeList = new ArrayList<>();
        grantTypeList.add("client_credentials");
        // 運行刷新token
        grantTypeList.add("refresh_token");
        baseClientDetails.setAuthorizedGrantTypes(grantTypeList);
        baseClientDetails.setRegisteredRedirectUri(Sets.newHashSet());
        // 不能夠設置爲null
        // baseClientDetails.setAuthorities(null);
        // 設置accessToken和refreshToken有效期
        baseClientDetails.setAccessTokenValiditySeconds(1000);
        baseClientDetails.setRefreshTokenValiditySeconds(1000);
        baseClientDetails.setAdditionalInformation(Maps.newHashMap());
        return baseClientDetails;
    }
}
複製代碼

在OAuth2AuthorizationServer中配置客戶端的帳戶的類

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private MyClientDetailsService myClientDetailsService;

    @Autowired
    private RedisTokenStore redisTokenStore;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
       // 配置客戶端的帳戶的類
        clients.withClientDetails(myClientDetailsService);     
    }
複製代碼

測試

curl -X POST "http://10.216.33.211:10808/com-oauth/oauth/token" --user clientapp:112233 -d "grant_type=client_credentials&scope=read_contacts 返回: {"access_token":"9ba822ce-3c1b-461f-85bb-722e83c30248","token_type":"bearer","expires_in":28068,"scope":"read_contacts"} 複製代碼

實現自定義用戶帳號

默認使用,使用內存用戶帳號

@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 接口,根據用戶名稱,若是驗證成功,則返回UserDetails 對象。實際應用將這裏增長訪問本身用戶帳號的功能便可。

@Service
	public class MyUserDetailsService implements UserDetailsService {
	    @Override
	    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
	        System.out.println("name=" + s);
	
	        // 獲取用戶權限列表
	        List<GrantedAuthority> authorities = new ArrayList();
	        authorities.add(new SimpleGrantedAuthority("admin"));
	        authorities.add(new SimpleGrantedAuthority("default"));
	        if(s.equals("hry")) {
	            String name = "hry";
	            String password = "1024";
	            User user = new User(name, password, true, true, true, true, authorities);
	            return user;
	        }else {
	            String name = "default";
	            String password = "1024";
	            User user = new User(name, password, true, true, true, true, authorities);
	            return user;
	        }
	    }
	}
複製代碼

@EnableWebSecurity 註解類,並實現WebSecurityConfigurerAdapter 的configure(AuthenticationManagerBuilder auth)設置UserDetailsService 的對象

@Configuration
	@EnableWebSecurity
	public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
	    @Autowired
	    private MyUserDetailsService myUserDetailsService;
	
	    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
	    @Override
	    protected AuthenticationManager authenticationManager() throws Exception {
	        return super.authenticationManager();
	    }
	
	    @Override
	    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	        super.configure(auth);
	        auth.userDetailsService(myUserDetailsService);
	    }
	}
	
複製代碼

將accesstoken存儲到redis中

token的存儲由TokenStore實現。TokenStore:存儲token的方式,經常使用有如下幾種實現

  • § InMemoryTokenStore:內存存儲
  • § JdbcTokenStore:數據庫存儲
  • § RedisTokenStore:redis存儲
  • § JwkTokenStore和JwtTokenStore

咱們使用RedisTokenStore實現redis存儲 @EnableAuthorizationServer:標記這是權限服務器

@Configuration
	//  enables the Authorization server configuration
	@EnableAuthorizationServer
	public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
	
	   @Autowired
	    private RedisTokenStore redisTokenStore;
	
	
	    @Override
	    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
	        super.configure(endpoints);
	        // 默認狀況配置在緩存中,若是配置這個,則存儲在redis中
	        endpoints.tokenStore(redisTokenStore);
	    }
	
	}

	 


複製代碼

初始化RedisTokenStore

@Bean
	    public RedisTokenStore createRedisTokenStore(RedisConnectionFactory connectionFactory){
	        return new RedisTokenStore(connectionFactory);
	    }
複製代碼
相關文章
相關標籤/搜索