一文了解SpringBoot Security的oauth2四種授權模式使用

項目結構:

1.pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.cxb</groupId>
    <artifactId>oauth2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-oauth2</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.3.0.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.application.properties

#management.security.enabled=true
#management.security.role=ADMIN
#spring.security.document.name=xiaomi
#spring.security.document.password=123

logging.level.org.springframework.security=debug
server.port=8080

# Redis數據庫索引(默認爲0)
spring.redis.database=0
# Redis服務器地址
spring.redis.host=127.0.0.1
# Redis服務器連接端口
spring.redis.port=6379
# Redis服務器連接密碼(默認爲空)
spring.redis.password=
# 連接池最大連接數(使用負值表示沒有限制)
spring.redis.pool.max-active=200
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
# 連接池中的最大空閒連接
spring.redis.pool.max-idle=10
# 連接池中的最小空閒連接
spring.redis.pool.min-idle=1
# 連接超時時間(毫秒)
spring.redis.timeout=10000
#是否在從池中取出連接前進行檢驗,如果檢驗失敗,則從池中去除連接並嘗試取出另一個
redis.testOnBorrow=true  
#在空閒時檢查有效性, 默認false
redis.testWhileIdle=true 
3.AuthorizationServerConfig
package com.cxb.oauth2.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Arrays;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private static final String RESOURCE_ID = "oauth-resource";
    private static final String CLIENT_ID = "client_id_1";
    private static final String CLIENT_SECRET = new BCryptPasswordEncoder().encode("123456");

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService detailsService;

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;


    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient(CLIENT_ID)
                .secret(CLIENT_SECRET)
                .resourceIds(RESOURCE_ID)
                .authorizedGrantTypes("password", "authorization_code", "implicit", "client_credentials", "refresh_token")
                .scopes("read","write")
                .accessTokenValiditySeconds(3600) // token失效時間
                .refreshTokenValiditySeconds(864000) //refresh token失效時間
                .redirectUris("http://example.com")
                .autoApprove("read");

    }

    /**
     * 認證服務端點配置
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.userDetailsService(detailsService)
                .tokenStore(memoryTokenStore())
                .authenticationManager(authenticationManager)
                //接收GET和POST
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);

    }

    /**
     * token存儲
     * @return
     */
    @Bean
    public TokenStore memoryTokenStore() {
        // 這是token存儲的InMemory方式
        // return new InMemoryTokenStore();

        //使用redis存儲token
        RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
        //設置redis token存儲中的前綴
        redisTokenStore.setPrefix("auth-token:");
        return redisTokenStore;
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer
                // 開啓/oauth/token_key驗證端口無權限訪問
                .tokenKeyAccess("permitAll()")
                // 開啓/oauth/check_token驗證端口認證權限訪問
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients();
        oauthServer.addTokenEndpointAuthenticationFilter(new CorsFilter(corsConfigurationSource()));

    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "HEAD", "DELETE", "OPTION"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.addExposedHeader("Authorization");
        configuration.addExposedHeader("Content-disposition");//文件下載消息頭
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
4.ResourceServerConfig
package com.cxb.oauth2.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;

@Slf4j
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    private static final String RESOURCE_ID = "oauth-resource";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(RESOURCE_ID).stateless(true);
    }

//    http://localhost:8080/oauth/authorize?response_type=code&client_id=client_id_1&redirect_uri=http://example.com
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.logout().deleteCookies("JSESSIONID");
        http.csrf().disable();
        http.formLogin().disable();
        http.sessionManagement().disable();
        http.cors();
        http.requestMatchers().antMatchers("/user/**")
                .and()
                .authorizeRequests().anyRequest().authenticated();

    }



}
5.WebSecurityConfig
package com.cxb.oauth2.config;

import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    @SneakyThrows
    public AuthenticationManager authenticationManagerBean() {
        return super.authenticationManagerBean();
    }


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    @Override
    @SneakyThrows
    public void configure(AuthenticationManagerBuilder auth) {
        auth.userDetailsService(userDetailsService());
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        http.formLogin().permitAll();
        http.logout().permitAll().deleteCookies("JSESSIONID");
        http.csrf().disable();
        http.authorizeRequests()
                .antMatchers( "/login").permitAll()
                .anyRequest().authenticated();
    }


    @Override
    @Bean
    @SneakyThrows
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
        userDetailsService.createUser(User.withUsername("admin")
                .password(passwordEncoder().encode("123456"))
                .authorities("ROLE_ADMIN").build());
        userDetailsService.createUser(User.withUsername("xiaoming")
                .password(passwordEncoder().encode("123456"))
                .authorities("ROLE_USER").build());
        return userDetailsService;
    }


}
6.DeptController
package com.cxb.oauth2.web;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author yutao
 * @create 2019-10-24 14:35
 **/
@RestController
@RequestMapping("dept")
public class DeptController {

    @GetMapping("get")
    @PreAuthorize("hasRole('ADMIN')")
    public Object get() {
        return "manage dept";
    }

    @GetMapping("get1")
    @PreAuthorize("hasRole('USER')")
    public Object get1() {
        return "it dept";

    }
}
7.UserController
package com.cxb.oauth2.web;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author yutao
 * @create 2019-10-24 14:35
 **/
@RestController
@RequestMapping("user")
public class UserController {

    @GetMapping("get")
    @PreAuthorize("hasRole('ADMIN')")
    public Object get() {
        return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }

    @GetMapping("get1")
    @PreAuthorize("hasRole('USER')")
    public Object get1() {
        return SecurityContextHolder.getContext().getAuthentication().getPrincipal();

    }
}
8.SpringbootOauth2Application
package com.cxb.oauth2;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class SpringbootOauth2Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootOauth2Application.class, args);
    }

}

9.測試:

### 密碼模式


```$xslt
localhost:8080/oauth/token?client_id=client_id_1&client_secret=123456&grant_type=password&username=admin&password=123456
{
    "access_token": "1e8ada36-ed82-49fc-9ca7-ff897d4bf2b2",
    "token_type": "bearer",
    "refresh_token": "9ba4580d-3497-4634-9696-af51a8b9bd41",
    "expires_in": 4,
    "scope": "all"
}
```

### 簡化模式
```$xslt
1、如下地址輸入,會重定向 http://localhost:8080/login
http://localhost:8080/oauth/authorize?response_type=token&client_id=client_id_1&redirect_uri=http://example.com&scope=write

2、http://localhost:8080/login 輸入賬號密碼,然後再次輸入
http://localhost:8080/oauth/authorize?response_type=token&client_id=client_id_1&redirect_uri=http://example.com&scope=write

3、獲取token
http://example.com/#access_token=3194ab36-e019-4557-8007-7a7fb2482542&token_type=bearer&expires_in=99http://example.com/#access_token=3194ab36-e019-4557-8007-7a7fb2482542&token_type=bearer&expires_in=99
 
```


### 授權碼模式

```$xslt
1、如下地址輸入,會重定向 http://localhost:8080/login
http://localhost:8080/oauth/authorize?response_type=code&client_id=client_id_1&redirect_uri=http://example.com&scope=write

2、http://localhost:8080/login 輸入賬號密碼,然後再次輸入
http://localhost:8080/oauth/authorize?response_type=code&client_id=client_id_1&redirect_uri=http://example.com&scope=write

3、http://example.com/?code=n9WlYp 獲取授權碼

4、http://localhost:8080/oauth/token?grant_type=authorization_code&client_id=client_id_1&client_secret=123456&redirect_uri=http://example.com&code=n9WlYp
{
    "access_token": "812403de-548d-4939-b0fa-57b1ec8abb11",
    "token_type": "bearer",
    "refresh_token": "8a8a7ebb-6f73-452d-9582-2fbf4ff80815",
    "expires_in": 99,
    "scope": "write"
}
```

### 客戶端模式

```
localhost:8080/oauth/token?client_id=client_id_1&client_secret=123456&grant_type=client_credentials
{
    "access_token": "c8ae1362-2084-4dbc-986c-847b7fe66f6c",
    "token_type": "bearer",
    "expires_in": 4,
    "scope": "all"
}
```

### refresh token

```$xslt
$ curl -X GET "localhost:8080/oauth/token?client_id=client_id_1&client_secret=123456&grant_type=refresh_token&refresh_token=fca82077-720e-46da-9c88-c2f221b0cb46"

{"access_token":"0274a51f-f61b-4bb0-9ae4-b99ef508c91e","token_type":"bearer","refresh_token":"fca82077-720e-46da-9c88-c2f221b0cb46","expires_in":99,"scope":"read write"}

```

### 使用token獲取資源

```$xslt
$ curl  -H "Authorization:bearer 284a5718-0a80-4eab-9d04-1bda3b6ceb62" -X GET  http://localhost:8080/user/get
{"error":"invalid_token","error_description":"Invalid access token: 284a5718-0a80-4eab-9d04-1bda3b6ceb62"}
或者
$ curl -X GET  http://localhost:8080/user/get?access_token=0f22ff90-1834-4654-9576-8737ec84d61e

```




刷新token:

文章參考代碼下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章