概要
之前的兩篇文章,講述了Spring Security 結合 OAuth2 、JWT 的使用,這一節要求對 OAuth2、JWT 有了解,若不清楚,先移步到下面兩篇提前瞭解下。
Spring Boot Security 整合 OAuth2 設計安全API接口服務
Spring Boot Security 整合 JWT 實現 無狀態的分佈式API接口
這一篇我們來實現 支持 JWT令牌 的授權服務器。
優點
使用 OAuth2 是向認證服務器申請令牌,客戶端拿這令牌訪問資源服務服務器,資源服務器校驗了令牌無誤後,如果資源的訪問用到用戶的相關信息,那麼資源服務器還需要根據令牌關聯查詢用戶的信息。
使用 JWT 是客戶端通過用戶名、密碼 請求服務器獲取 JWT,服務器判斷用戶名和密碼無誤之後,可以將用戶信息和權限信息經過加密成 JWT 的形式返回給客戶端。在之後的請求中,客戶端攜帶 JWT 請求需要訪問的資源,如果資源的訪問用到用戶的相關信息,那麼就直接從JWT中獲取到。
所以,如果我們在使用 OAuth2 時結合JWT ,就能節省集中式令牌校驗開銷,實現無狀態授權認證。
快速上手
項目說明
工程名端口作用jwt-authserver8080授權服務器jwt-resourceserver8081資源服務器
pom.xml
<code><dependency> <groupid>org.springframework.boot/<groupid> <artifactid>spring-boot-starter-oauth2-resource-server/<artifactid>/<dependency><dependency> <groupid>org.springframework.boot/<groupid> <artifactid>spring-boot-starter-security/<artifactid>/<dependency><dependency> <groupid>org.springframework.boot/<groupid> <artifactid>spring-boot-starter-oauth2-client/<artifactid>/<dependency><dependency> <groupid>org.springframework.security.oauth.boot/<groupid> <artifactid>spring-security-oauth2-autoconfigure/<artifactid> <version>2.1.3.RELEASE/<version>/<dependency><dependency> <groupid>org.springframework.security/<groupid> <artifactid>spring-security-jwt/<artifactid> <version>1.0.10.RELEASE/<version>/<dependency><dependency> <groupid>org.springframework.boot/<groupid> <artifactid>spring-boot-starter-web/<artifactid>/<dependency>/<code>
WebSecurityConfig
<code>@Configurationpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http. authorizeRequests().antMatchers("/**").permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("user").password("123456").roles("USER"); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public PasswordEncoder passwordEncoder() { return new PasswordEncoder() { @Override public String encode(CharSequence charSequence) { return charSequence.toString(); } @Override public boolean matches(CharSequence charSequence, String s) { return Objects.equals(charSequence.toString(),s); } }; }}/<code>
為了方便,使用內存模式,在內存中創建一個用戶 user 密碼 123456。
OAuth2AuthorizationServer
<code>/** * 授權服務器 */@Configuration@EnableAuthorizationServerpublic class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter { /** * 注入AuthenticationManager ,密碼模式用到 */ @Autowired private AuthenticationManager authenticationManager; /** * 對Jwt簽名時,增加一個密鑰 * JwtAccessTokenConverter:對Jwt來進行編碼以及解碼的類 */ @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("test-secret"); return converter; } /** * 設置token 由Jwt產生,不使用默認的透明令牌 */ @Bean public JwtTokenStore jwtTokenStore() { return new JwtTokenStore(accessTokenConverter()); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .authenticationManager(authenticationManager) .tokenStore(jwtTokenStore()) .accessTokenConverter(accessTokenConverter()); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientapp") .secret("123") .scopes("read") //設置支持[密碼模式、授權碼模式、token刷新] .authorizedGrantTypes( "password", "authorization_code", "refresh_token"); }}/<code>
資源服務器
pom.xml
<code><dependency> <groupid>org.springframework.boot/<groupid> <artifactid>spring-boot-starter-security/<artifactid>/<dependency><dependency> <groupid>org.springframework.boot/<groupid> <artifactid>spring-boot-starter-oauth2-resource-server/<artifactid>/<dependency><dependency> <groupid>org.springframework.boot/<groupid> <artifactid>spring-boot-starter-oauth2-client/<artifactid>/<dependency><dependency> <groupid>org.springframework.security.oauth.boot/<groupid> <artifactid>spring-security-oauth2-autoconfigure/<artifactid> <version>2.1.3.RELEASE/<version>/<dependency><dependency> <groupid>org.springframework.security/<groupid> <artifactid>spring-security-jwt/<artifactid> <version>1.0.10.RELEASE/<version>/<dependency><dependency> <groupid>org.springframework.boot/<groupid> <artifactid>spring-boot-starter-web/<artifactid>/<dependency>/<code>
HelloController
<code>@RestController("/api")public class HelloController { @PostMapping("/api/hi") public String say(String name) { return "hi , " + name; }}/<code>
OAuth2ResourceServer
<code>/** * 資源服務器 */@Configuration@EnableResourceServerpublic class OAuth2ResourceServer extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated().and() .requestMatchers().antMatchers("/api/**"); }}/<code>
application.yml
<code>server: port: 8081security: oauth2: resource: jwt: key-value: test-secret/<code>
參數說明:
- security.oauth2.resource.jwt.key-value:設置簽名key 保持和授權服務器一致。
- security.oauth2.resource.jwt:項目啟動過程中,檢查到配置文件中有 security.oauth2.resource.jwt 的配置,就會生成 jwtTokenStore 的 bean,對令牌的校驗就會使用 jwtTokenStore 。
驗證
請求令牌
<code>curl -X POST --user 'clientapp:123' -d 'grant_type=password&username=user&password=123456' http://localhost:8080/oauth/token/<code>
返回JWT令牌
<code>{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTQ0MzExMDgsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiOGM0YWMyOTYtMDQwYS00Y2UzLTg5MTAtMWJmNjZkYTQwOTk3IiwiY2xpZW50X2lkIjoiY2xpZW50YXBwIiwic2NvcGUiOlsicmVhZCJdfQ.YAaSRN0iftmlR6Khz9UxNNEpHHn8zhZwlQrCUCPUmsU", "token_type": "bearer", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsicmVhZCJdLCJhdGkiOiI4YzRhYzI5Ni0wNDBhLTRjZTMtODkxMC0xYmY2NmRhNDA5OTciLCJleHAiOjE1NTY5Nzk5MDgsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiI0ZjA5M2ZjYS04NmM0LTQxZWUtODcxZS1kZTY2ZjFhOTI0NTAiLCJjbGllbnRfaWQiOiJjbGllbnRhcHAifQ.vvAE2LcqggBv8pxuqU6RKPX65bl7Zl9dfcoIbIQBLf4", "expires_in": 43199, "scope": "read", "jti": "8c4ac296-040a-4ce3-8910-1bf66da40997"}/<code>
攜帶JWT令牌請求資源
<code>curl -X POST -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTQ0MzExMDgsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiOGM0YWMyOTYtMDQwYS00Y2UzLTg5MTAtMWJmNjZkYTQwOTk3IiwiY2xpZW50X2lkIjoiY2xpZW50YXBwIiwic2NvcGUiOlsicmVhZCJdfQ.YAaSRN0iftmlR6Khz9UxNNEpHHn8zhZwlQrCUCPUmsU" -d 'name=zhangsan' http://localhost:8081/api/hi/<code>
返回
<code>hi , zhangsan/<code>
源碼
https://github.com/gf-huanchupk/SpringBootLearning/tree/master/springboot-security-oauth2-jwt
閱讀更多 程序員果果 的文章