Spring Boot OAuth2從0到1

2、開發流程

1.一、知識來源

OAuth2開發指導:https://projects.spring.io/spring-security-oauth/docs/oauth2.html Spring Boot下的OAuth2使用:https://docs.spring.io/spring-security-oauth2-boot/docs/2.2.0.RELEASE/reference/html5/ Spring Cloud Security: 博客參考1:http://www.javashuo.com/article/p-kdetiktk-de.htmlhtml

1.二、使用Spring Boot初始化項目工具,勾選Web

當前版本是:Spring Boot2.2.0.RELEASE,Spring Cloud Hoxton.M3 因爲Spring Security OAuth2並不在Spring Boot中維護,因此不能在Spring Boot中自動引入依賴 可是Spring Cloud中好像維護了Spring Security OAuth2的版本 生成的工程自動引入了spring-cloud-starter-oauth2依賴,該依賴中包含了spring-security-oauth2-autoconfigure依賴 手動加入spring-security-oauth2-autoconfigure依賴前端

1.三、參照Spring Boot下OAuth2的指引

增長@EnableAuthorizationServer註解html5

@EnableAuthorizationServer
@SpringBootApplication
public class SimpleAuthorizationServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SimpleAuthorizationServerApplication, args);
    }
}
  • 定義一個客戶端和密碼
security:
  oauth2:
    client:
      client-id: first-client
      client-secret: noonewilleverguess

獲取token: curl client:pwd@localhost:8080/oauth/token -d grant_type=client_credentials -d scope=anyjava

1.四、根據【OAuth2開發指導】配置AuthorizationServerConfigurerAdapter中客戶端明細

@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	// 配置客戶端明細
	@SneakyThrows
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) {
		// 一、基於內存的客戶端明細
		clients.inMemory().withClient("client")// 容許訪問的客戶端
				.secret("{noop}pwd")// 密碼
				.authorizedGrantTypes("refresh_token", "password", "client_credentials")// 容許的受權類型
				.scopes("webclient", "mobileclient");// 引用程序做用域
	}
}

而後經過客戶端密碼模式獲取token curl client:pwd@localhost:8080/oauth/token -d grant_type=client_credentials -d scope=webclient 注意,新版本的Spring Security5中,密碼增長了前綴{noop},能夠指定密碼的加密內容,只有服務端須要加該前綴,前端傳的是加密後的密碼mysql

修改自定義實現獲取客戶端明細 分別引入jdbc、mysql、druid依賴,這裏藉助了JdbcClientDetailsService這個實現,進行了擴展,稍微修改了SQL,以及加入了緩存機制 注意一點:這裏的sql中,使用了CONCAT('{noop}',client_secret),表明這裏的密碼是明文存儲的,之後有須要能夠修改這裏 再次經過客戶端密碼模式獲取token,客戶端帳號密碼、scope,按數據庫存儲的輸入,便可獲取獲得 curl client:pwd@localhost:8080/oauth/token -d grant_type=client_credentials -d scope=webclientweb

1.五、根據【Spring Boot下的OAuth2使用】配置使用password模式下,用戶的帳號信息獲取

參考https://docs.spring.io/spring-security-oauth2-boot/docs/2.2.0.RELEASE/reference/html5/#oauth2-boot-authorization-server-password-grant-authentication-configuration 要使用OAuth2的Password模式,有多種方式,因爲咱們使用AuthorizationServerConfigurerAdapter,符合1.7.3所說,以下便可實現spring

  • AuthorizationServerEndpointsConfigurer綁定Security的authenticationManager
  • 而後建立一個UserDetailsService的Bean
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	@Autowired
	private AuthenticationConfiguration authenticationConfiguration;

	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		endpoints.authenticationManager(authenticationConfiguration.getAuthenticationManager())
	}
}
@Service
public class HopeUserDetailsService implements UserDetailsService {
	@Override
	@SneakyThrows
	public UserDetails loadUserByUsername(String username) {
		
		Collection<? extends GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("role1");
		return new HopeUser(1, 1, 1, "hope", "{noop}hope", true, true, true, true,authorities );
	}
}

curl client:pwd@localhost:8080/oauth/token -d grant_type=password -d scope=webclient -d username=hope -d password=hope 便可獲取到tokensql

1.七、測試驗證token端點

默認驗證token端點是/oauth/check_token 先配置驗證端點徹底開放數據庫

@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	@Override
	public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
		oauthServer
				.checkTokenAccess("permitAll()");
	}
}

curl client:pwd@localhost:8080/oauth/check_token -d token=8313a24b-c0d2-437b-afb0-0bb4fd440f47 返回令牌中攜帶信息緩存

1.八、保護資源服務器

使用Spring Boot初始化項目工具,勾選Web 手動加入spring-security-oauth2-autoconfigure依賴 使用@EnableResourceServer註解 配置文件以下

security:
  oauth2:
    resource:
      token-info-uri: http://localhost:8080/oauth/check_token
    client:
      # 配置後才能認證,生產環境建議設置獨立的客戶端信息
      client-id: test
      client-secret: test
      scope: server

注意,須要開放受權服務器的check_token端點

@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	@Override
	public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
		oauthServer
				.checkTokenAccess("isAuthenticated()");
	}
}

此時,直接訪問資源提示沒有權限 訪問受權服務器,獲取token,使用bear token訪問這個受保護資源,訪問成功!

1.九、使用JWT Token

spring-security-oauth2-autoconfigure已包含jwt依賴,因此不須要處理依賴 受權服務器: 經過AuthorizationServerEndpointsConfigurer配置.accessTokenConverter(jwtAccessTokenConverter),配置一個JwtAccessTokenConverter便可,具體方法下面討論 資源服務器: 配置application配置文件便可。

2者成組配置:

方式1:對稱加密 受權服務器:

@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
	JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
	converter.setSigningKey("micosrv_signing_key");
	return converter;
}

資源服務器:

security:
  oauth2:
    resource:
      jwt:
        key-value: micosrv_signing_key

方式2:非對稱加密(自行賦值) 受權服務器:

config:
    oauth2:
        # openssl genrsa -out jwt.pem 2048
        # openssl rsa -in jwt.pem
        privateKey: |
            -----BEGIN RSA PRIVATE KEY-----
            MIIEowIBAAKCAQEAm4irSNcR7CSSfXconxL4g4M4j34wTWdTv93ocMn4VmdB7rCB
            U/BlxXtBUf/cgLIgQhQrAPszSZSmxiEXCOkGPr4aQBQuPgmNIR95Dhbzw/ZN0Bne
            cAt3ZfkkDBHv8kH3kR/jYGTdwrxKeDgXGljNsTRhbjuASxPG/Z6gU1yRPCsgc2r8
            NYnztWGcDWqaobqjG3/yzFmusoAboyV7asIpo4yk378LmonDNwxnOOTb2Peg5Pee
            lwfOwJPbftK1VOOt18zA0cchw6dHUzq9NlB8clps/VdBap9BxU3/0YoFXRIc18ny
            zrWo2BcY2KQqX//AJC3OAfrfDmo+BGK8E0mp8wIDAQABAoIBAENp64P45GXMPEpx
            eYPpfxnRqJRZh6olHSHOl087243n16YTjxrI2fPMxrU6B2Mo0d6SS0lzl/lOmzLJ
            aOiNyA0t7MbVeG2fSjKPJ7M5s5K+kV+fttAtyCTE5iDtLWl9ukaG4dEIJy6e2lBd
            T3Y2A4HJSGm1FJh2DAwl0ywOtUy0X6ki9DgXVAaCGDuoU25Rhun64dh802DZbEEJ
            LdorIyeJ0ovCZyNvhlZRYkAOPy3k88smYl2jE/AbZ7pCKz/XggDcjNsERm2llaa3
            pNTAZQUlHu0BQrCn6J9BxtMPyduiyrE+JYqTwnYhWQ5QRe/2J8O3t0eIK9TfUQpJ
            DrZf00ECgYEAy/sLX8UCmERwMuaQSwoM0BHTZIc0iAsgiXbVOLua9I3Tu/mXOVdH
            TikjdoWLqM62bA9dN/oqzHDwvqCy6zwamjFVSmJUejf5v+52Qj64leOmDX/RC4ne
            L08N1nP/Y4X24Y/5zq18qvVlhOMDdydzayJFrGhkQKhJg58pRUIdenECgYEAwzLC
            Awr3LeUlHa+d2O6siJVmljTc8lT+qX4TvqTDH8rAC/EyKMNaTjaX6mWosZZ7qYXv
            EMxvQzTEzUHRXrCGlhbX8xiBlWnvpghF2GJEvP9WaU/+OCr0gItRSLPDuZ6ctzKb
            3QkBEiC8ODyPRKzlA67D23S3KJB067IUV81h9KMCgYBXUqmT3is2NFYz9DBhb3P8
            vyTYLGl4tArBznWJTAcSGoVCO59ZlNuZwlLEMnePVK8To6AsjpQz4UWu1ezCd4CL
            8gKpTV8M01m/qL5HrcInqMU1kjpTzjmn1xf9brsuR/NgrNoseGieZ1+GfAjHwcPP
            YWSiYi5I38JY7pIkbCFigQKBgAnVtty8YrPXRcV3IbbaX6sKC/8pbrBvA926Unha
            iNJDPuXbIzHWleg26/SNZrB76oMiEmeARWLXd8r3s/rXXhCV2g+PfofurHprFEnQ
            ubHkE5B+zUo7L9KCMng9RnFFwpOgYyYB3CHzsEgNFRLauzcySP/3o3rRvHJbqJa7
            7GGNAoGBAKSBn4zq0iNWI2BUBb90icMsHEneiydGtFcEl3/Sz8vmjFZn0sjRbGoY
            gmP9LlQ+o7xRiJ/LTesi5BA6zCGrcdp0aeyJzCRbFc3WqjGeyLbfx1sJVVB6PnvS
            iKvvCOJq6kl3/opO+ybqJ8dzkEyoj8K4+fcX1+U6eW2w+vSpOosG
            -----END RSA PRIVATE KEY-----
        # openssl rsa -in jwt.pem -pubout
        publicKey: |
            -----BEGIN PUBLIC KEY-----
            MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm4irSNcR7CSSfXconxL4
            g4M4j34wTWdTv93ocMn4VmdB7rCBU/BlxXtBUf/cgLIgQhQrAPszSZSmxiEXCOkG
            Pr4aQBQuPgmNIR95Dhbzw/ZN0BnecAt3ZfkkDBHv8kH3kR/jYGTdwrxKeDgXGljN
            sTRhbjuASxPG/Z6gU1yRPCsgc2r8NYnztWGcDWqaobqjG3/yzFmusoAboyV7asIp
            o4yk378LmonDNwxnOOTb2Peg5PeelwfOwJPbftK1VOOt18zA0cchw6dHUzq9NlB8
            clps/VdBap9BxU3/0YoFXRIc18nyzrWo2BcY2KQqX//AJC3OAfrfDmo+BGK8E0mp
            8wIDAQAB
            -----END PUBLIC KEY-----
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
	// 自行生成公私鑰
	JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
	converter.setSigningKey(privateKey);
	converter.setVerifierKey(publicKey);
	return converter;
}

資源服務器:

security:
  oauth2:
    resource:
      jwt:
        key-value: 
            -----BEGIN PUBLIC KEY-----
            MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm4irSNcR7CSSfXconxL4
            g4M4j34wTWdTv93ocMn4VmdB7rCBU/BlxXtBUf/cgLIgQhQrAPszSZSmxiEXCOkG
            Pr4aQBQuPgmNIR95Dhbzw/ZN0BnecAt3ZfkkDBHv8kH3kR/jYGTdwrxKeDgXGljN
            sTRhbjuASxPG/Z6gU1yRPCsgc2r8NYnztWGcDWqaobqjG3/yzFmusoAboyV7asIp
            o4yk378LmonDNwxnOOTb2Peg5PeelwfOwJPbftK1VOOt18zA0cchw6dHUzq9NlB8
            clps/VdBap9BxU3/0YoFXRIc18nyzrWo2BcY2KQqX//AJC3OAfrfDmo+BGK8E0mp
            8wIDAQAB
            -----END PUBLIC KEY-----

方式3:非對稱加密(經過token_key端點) 受權服務器: 在方式2的基礎上,須要配置開放token_key端口,在AuthorizationServerConfigurerAdapter中配置

@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
	oauthServer.tokenKeyAccess("permitAll()");
}

資源服務器:

security:
  oauth2:
    resource:
      jwt:
        key-uri: http://localhost:8080/oauth/token_key

1.十、token傳遞

客戶端添加依賴:alibaba discovery,alibaba config,hystricx斷路器,feign依賴,spring-cloud-starter-security 而且管理版本:spring-cloud-alibaba-dependencies 0.9.0.RELEASE 使用@SpringCloudApplication註解,開啓Cloud App,支持服務註冊於發現和斷路器 使用@EnableFeignClients註解,支持Feign客戶端 配置一個OAuth2FeignRequestInterceptor攔截器,經過AccessTokenContextRelay來傳遞token AccessTokenContextRelay在@EnableOAuth2Client中有配置,可是咱們這裏不做爲一個OAuth2客戶端來看,能夠本身建立一個AccessTokenContextRelay 調用Feign,便可看到token是否能正常傳遞

1.十一、JWT過於龐大,應該使用令牌內省這個概念

使用原來的令牌,在資源服務端訪問受權服務器的時候,進行緩存,下次訪問的時候取緩存 參考文章: 冷總:https://cloud.tencent.com/developer/article/1435727 冷總:https://my.oschina.net/giegie/blog/3023768 http://www.javashuo.com/article/p-ecugkcoa-ke.html 思路

  • 參考冷總第二篇文章,使用JWT,而後本身調用check_token,再解析
  • 使用原始的訪問check_token方案,注入本身的RestTemplate,增長攔截器,進行緩存和處理

1.十二、密碼的加密

pig中應該是網關進行了加密和解密,OAuth2服務器接收的應該是原生密碼,而後經過BCRYPT加密,與數據庫中已通過BCRYPT加密的密碼進行對比

1.1三、check_token添加自定義信息

返回值是經過AccessTokenConverter來進行轉換的,我繼承DefaultAccessTokenConverter,並在convertAccessToken中,增長自定義的信息 受權服務器:在AuthorizationServerConfigurerAdapter中注入AccessTokenConverter 資源服務器:在ResourceServerTokenServices中注入AccessTokenConverter

相關文章
相關標籤/搜索