SpringSecurity整合OAuth2
Spring-Security整合OAuth2
基本配置
1、pom.xml
spring-security-oauth2
spring-boot-starter-security
spring-boot-starter-data-redis
<?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.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.lx</groupId>
<artifactId>security-oauth</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>security-oauth</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.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.6.RELEASE</version>
</dependency>
<!--redis 存储token-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、yaml配置
spring:
# redis配置
redis:
port: 6379
password: xxxxxx
host: 47.96.141.44
database: 0
3、controller
@RestController
public class controller {
/**
* 模拟admin用户
* @return
*/
@GetMapping("/admin/hello")
public String admin() {
return "hello admin";
}
/**
* 模拟user用户
* @return
*/
@GetMapping("/user/hello")
public String user() {
return "user hello";
}
/**
* 普通用户(登录即可访问)
* @return
*/
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
4、核心配置
①授权服务器
/**
* 授权服务器
*/
@Configuration
//开启授权服务器
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
/**
* 注入认证管理器 来支持password的认证模式
*
* 在oauth2模式中,有四种不同的认证模式.
* 第三方登录一般使用授权码模式
* 前后端分离一般使用password模式
*/
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Autowired
private UserDetailsService userDetailsService;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 配置password模式
* 配置用户
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 配置在内存中
clients.inMemory()
// 设置认证模式 设置客户端id
.withClient("password")
// 配置授权模式 标准的oauth并不包含refresh_token。但是在springsecurity实现下refresh_token归为其中一种
// 获取token 和 刷新token的
.authorizedGrantTypes("password", "refresh_token")
// 设置过期时间 1800s
.accessTokenValiditySeconds(1800)
// 设置资源id
.resourceIds("rid")
.scopes("all")
// 需要的密码(加密之后)密码 123
.secret("$2a$10$vMu/taZjUE/JPUUi8Aep/epAjQjT5XqJj1/fOKqH/n1k9x8DOnkW6");
}
/**
* 令牌的存储
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
/**
*支持client id和 client secret 作登录认证
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients();
}
}
②资源服务器(提供资源,即访问的路径,引用的令牌)
/**
* 资源服务器 提供资源
*/
@Configuration
//开启资源服务器
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter{
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
// 指定资源id
resources.resourceId("rid")
// 这些资源基于令牌进行认证
.stateless(true);
}
/**
* 先去授权服务器获取token,再去访问资源
* 在这里我们提供访问资源的路径
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated();
}
}
③security配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
protected UserDetailsService userDetailsService() {
return super.userDetailsService();
}
/**
* 配置模拟用户
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
//密码123 .withUser("lc").password("$2a$10$vMu/taZjUE/JPUUi8Aep/epAjQjT5XqJj1/fOKqH/n1k9x8DOnkW6")
.roles("admin")
.and()
//密码123 .withUser("zs").password("$2a$10$vMu/taZjUE/JPUUi8Aep/epAjQjT5XqJj1/fOKqH/n1k9x8DOnkW6")
.roles("user");
}
/**
* 拦截指定的请求
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/oauth/**")
// 拦截像/oauth/**的请求
.authorizeRequests()
// 符合/oauth/**
.antMatchers("/oauth/**")
// 全部放行
.permitAll()
.and()
.csrf().disable();
}
}
5、请求过程详解
① 获取令牌
PUT: http://localhost:8080/oauth/token
content-type : x-www-form-urlencoded
请求参数==>
username 和 password 为登录的用户名和密码
"key":"username","value":"lc"
"key":"password","value":"123"
"key":"grant_type","value":"password"
"key":"client_id","value":"password"
"key":"scope","value":"all"
"key":"client_secret","value":"123"
结果: ==>
{
"access_token": "d274cd88-1c84-4d6a-9da4-11ed734aed6f",
"token_type": "bearer",
//用户请求获得新的token的验证
"refresh_token": "0a454a83-9bf5-4331-bf12-35d4ac519f30",
//过期时间
"expires_in": 1064,
"scope": "all"
}
②请求资源接口
访问普通接口==>
GET: http://localhost:8080/hello?access_token=d274cd88-1c84-4d6a-9da4-11ed734aed6f
admin接口==>
GET: http://localhost:8080/admin/hello?access_token=d274cd88-1c84-4d6a-9da4-11ed734aed6f
user接口==>
GET: http://localhost:8080/user/hello?access_token=d274cd88-1c84-4d6a-9da4-11ed734aed6f
③获取新的token,旧token失效
POST: http://localhost:8080/oauth/token
请求参数==>
refresh_token 是之前用户登录 refresh_token的的值
"key":"grant_type","value":"refresh_token"
"key":"refresh_token","value":"0a454a83-9bf5-4331-bf12-35d4ac519f30"
"key":"client_id","value":"password"
"key":"client_secret","value":"123"
请求结果==>
{
"access_token": "8b03dd6a-18c7-49c0-b2ab-e0962627608c",
"token_type": "bearer",
"refresh_token": "0a454a83-9bf5-4331-bf12-35d4ac519f30",
"expires_in": 1799,
"scope": "all"
}