Spring Boot Security OAuth2 實現支持JWT令牌的受權服務器

概要

以前的兩篇文章,講述了Spring Security 結合 OAuth2 、JWT 的使用,這一節要求對 OAuth二、JWT 有了解,若不清楚,先移步到下面兩篇提早了解下。java

Spring Boot Security 整合 OAuth2 設計安全API接口服務git

Spring Boot Security 整合 JWT 實現 無狀態的分佈式API接口github

這一篇咱們來實現 支持 JWT令牌 的受權服務器。web

優勢

使用 OAuth2 是向認證服務器申請令牌,客戶端拿這令牌訪問資源服務服務器,資源服務器校驗了令牌無誤後,若是資源的訪問用到用戶的相關信息,那麼資源服務器還須要根據令牌關聯查詢用戶的信息。spring

使用 JWT 是客戶端經過用戶名、密碼 請求服務器獲取 JWT,服務器判斷用戶名和密碼無誤以後,能夠將用戶信息和權限信息通過加密成 JWT 的形式返回給客戶端。在以後的請求中,客戶端攜帶 JWT 請求須要訪問的資源,若是資源的訪問用到用戶的相關信息,那麼就直接從JWT中獲取到。shell

因此,若是咱們在使用 OAuth2 時結合JWT ,就能節省集中式令牌校驗開銷,實現無狀態受權認證。json

快速上手

項目說明

工程名 端口 做用
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。api

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 。

驗證

請求令牌springboot

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
複製代碼

源碼

github.com/gf-huanchup…

相關文章
相關標籤/搜索