概要
之前的兩篇文章,講述了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-authserver | 8080 | 授權伺服器 |
jwt-resourceserver | 8081 | 資源伺服器 |
授權伺服器
pom.xml
<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>
複製程式碼
WebSecurityConfig
@Configuration
public 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);
}
};
}
}
複製程式碼
為了方便,使用記憶體模式,在記憶體中建立一個使用者 user 密碼 123456。
OAuth2AuthorizationServer
/**
* 授權伺服器
*/
@Configuration
@EnableAuthorizationServer
public 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");
}
}
複製程式碼
資源伺服器
pom.xml
<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>
複製程式碼
HelloController
@RestController("/api")
public class HelloController {
@PostMapping("/api/hi")
public String say(String name) {
return "hi , " + name;
}
}
複製程式碼
OAuth2ResourceServer
/**
* 資源伺服器
*/
@Configuration
@EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated().and()
.requestMatchers().antMatchers("/api/**");
}
}
複製程式碼
application.yml
server:
port: 8081
security:
oauth2:
resource:
jwt:
key-value: test-secret
複製程式碼
引數說明:
- security.oauth2.resource.jwt.key-value:設定簽名key 保持和授權伺服器一致。
- security.oauth2.resource.jwt:專案啟動過程中,檢查到配置檔案中有 security.oauth2.resource.jwt 的配置,就會生成 jwtTokenStore 的 bean,對令牌的校驗就會使用 jwtTokenStore 。
驗證
請求令牌
curl -X POST --user 'clientapp:123' -d 'grant_type=password&username=user&password=123456' http://localhost:8080/oauth/token
複製程式碼
返回JWT令牌
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTQ0MzExMDgsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiOGM0YWMyOTYtMDQwYS00Y2UzLTg5MTAtMWJmNjZkYTQwOTk3IiwiY2xpZW50X2lkIjoiY2xpZW50YXBwIiwic2NvcGUiOlsicmVhZCJdfQ.YAaSRN0iftmlR6Khz9UxNNEpHHn8zhZwlQrCUCPUmsU",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsicmVhZCJdLCJhdGkiOiI4YzRhYzI5Ni0wNDBhLTRjZTMtODkxMC0xYmY2NmRhNDA5OTciLCJleHAiOjE1NTY5Nzk5MDgsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiI0ZjA5M2ZjYS04NmM0LTQxZWUtODcxZS1kZTY2ZjFhOTI0NTAiLCJjbGllbnRfaWQiOiJjbGllbnRhcHAifQ.vvAE2LcqggBv8pxuqU6RKPX65bl7Zl9dfcoIbIQBLf4",
"expires_in": 43199,
"scope": "read",
"jti": "8c4ac296-040a-4ce3-8910-1bf66da40997"
}
複製程式碼
攜帶JWT令牌請求資源
curl -X POST -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTQ0MzExMDgsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiOGM0YWMyOTYtMDQwYS00Y2UzLTg5MTAtMWJmNjZkYTQwOTk3IiwiY2xpZW50X2lkIjoiY2xpZW50YXBwIiwic2NvcGUiOlsicmVhZCJdfQ.YAaSRN0iftmlR6Khz9UxNNEpHHn8zhZwlQrCUCPUmsU" -d 'name=zhangsan' http://localhost:8081/api/hi
複製程式碼
返回
hi , zhangsan
複製程式碼