一文了解SpringBoot Security的oauth2四种授权模式使用

项目结构:

一文了解SpringBoot Security的oauth2四种授权模式使用_第1张图片

1.pom.xml



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.3.0.RELEASE
         
    
    com.cxb
    oauth2
    0.0.1-SNAPSHOT
    springboot-oauth2
    Demo project for Spring Boot

    
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter-security
        

        
            org.springframework.security.oauth
            spring-security-oauth2
            2.3.3.RELEASE
        

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
            org.projectlombok
            lombok
            1.18.12
        

        
            org.springframework.boot
            spring-boot-starter-data-redis
            2.3.0.RELEASE
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    


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

```




一文了解SpringBoot Security的oauth2四种授权模式使用_第2张图片

一文了解SpringBoot Security的oauth2四种授权模式使用_第3张图片

刷新token:

一文了解SpringBoot Security的oauth2四种授权模式使用_第4张图片

一文了解SpringBoot Security的oauth2四种授权模式使用_第5张图片

文章参考、代码下载

你可能感兴趣的:(springboot,oauth2,redis)