文章首發(fā)于公眾號《程序員果果》
地址:https://mp.weixin.qq.com/s/UvRfEF_WW1TGQqsuMxgoLg
本篇源碼:https://github.com/gf-huanchupk/SpringBootLearning
概要
之前的兩篇文章功氨,講述了Spring Security 結合 OAuth2 、JWT 的使用手幢,這一節(jié)要求對 OAuth2捷凄、JWT 有了解,若不清楚围来,先移步到下面兩篇提前了解下跺涤。
Spring Boot Security 整合 OAuth2 設計安全API接口服務
Spring Boot Security 整合 JWT 實現(xiàn) 無狀態(tài)的分布式API接口
這一篇我們來實現(xiàn) 支持 JWT令牌 的授權服務器。
優(yōu)點
使用 OAuth2 是向認證服務器申請令牌监透,客戶端拿這令牌訪問資源服務服務器钦铁,資源服務器校驗了令牌無誤后,如果資源的訪問用到用戶的相關信息才漆,那么資源服務器還需要根據(jù)令牌關聯(lián)查詢用戶的信息牛曹。
使用 JWT 是客戶端通過用戶名、密碼 請求服務器獲取 JWT醇滥,服務器判斷用戶名和密碼無誤之后黎比,可以將用戶信息和權限信息經過加密成 JWT 的形式返回給客戶端。在之后的請求中鸳玩,客戶端攜帶 JWT 請求需要訪問的資源阅虫,如果資源的訪問用到用戶的相關信息,那么就直接從JWT中獲取到不跟。
所以颓帝,如果我們在使用 OAuth2 時結合JWT ,就能節(jié)省集中式令牌校驗開銷,實現(xiàn)無狀態(tài)授權認證购城。
快速上手
項目說明
工程名 | 端口 | 作用 |
---|---|---|
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);
}
};
}
}
為了方便吕座,使用內存模式,在內存中創(chuàng)建一個用戶 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
參數(shù)說明:
- 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
源碼
https://github.com/gf-huanchupk/SpringBootLearning/tree/master/springboot-security-oauth2-jwt