Spring Security OAuth2
OAuth 是一个开放标准,采用令牌的方式可以让用户灵活的对第三方应用授权或者收回权限。
OAuth 2 角色
- 资源所有者: 即用户, 具有头像、照片、视频等资源
- 客户端: 即第三方应用
- 授权服务器: 用来验证用户提供的信息是否正确, 并返回一个令牌给第三方应用
- 资源服务器: 提供用户资源的服务器,例如头像、照片、视频等
一般来说, 授权服务器和资源服务器可以是同一台服务器
OAuth 2 授权流程
OAuth 2 授权模式
- 授权码模式: 功能完整、流程最严谨的授权模式。 他的特点就是通过客户端的服务器与授权服务器进行交互
- 简化模式: 不需要客户端服务器参与, 直接在浏览器中向授权服务器申请令牌
- 密码模式: 用户把用户名密码直接告诉客户端, 客户端使用这些信息向授权服务器申请令牌
- 客户端模式: 客户端使用自己的名义而不是用户的名义向服务器提供者申请授权
实践
依赖
<!-- Redis 排除Lettuce 引入Jedis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
<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-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
将令牌存入Redis, 所以添加Redis 依赖
redis配置:
spring:
# redis 配置
redis:
database: 0
host: xx.xxx.xxx
port: xxx
password: xxx
jedis:
pool:
max-active: 8
max-idle: 8
max-wait: -1ms
min-idle: 0
配置授权服务器
授权服务器和资源服务器可以是同一台服务器
/**
* 授权服务器配置
*
* @author iscolt
* @date 2019/07/12
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig
extends AuthorizationServerConfigurerAdapter {
// 该对象用来支持password模式
@Autowired
AuthenticationManager authenticationManager;
// 用来缓存, 将token存到redis中
@Autowired
RedisConnectionFactory redisConnectionFactory;
// 为刷新对象提供支持
@Autowired
UserDetailsService userDetailsService;
// 密码加密
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 配置password授权模式
clients.inMemory()
.withClient("password")
.authorizedGrantTypes("password")
.accessTokenValiditySeconds(1800)
.resourceIds("rid")
.scopes("all")
.secret("$2a$10$WtukGtXlkDrqDrH.0pUdXOi6NBN0exY8o6Upj3E1d4IK4hgT3fPuO");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 配置令牌存储
endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// 配置支持client_id和client_security支持
security.allowFormAuthenticationForClients();
}
}
配置资源服务器
/**
* 资源服务器配置
*
* @author iscolt
* @date 2019/07/12
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig
extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
// 配置资源id, 和授权服务器资源id一致
resources.resourceId("rid").stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
// 配置HttpSecurity
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated();
}
}
配置Security
/**
* 配置Security
*
* @author iscolt
* @date 2019/07/12
*/
@Configuration
public class WebSecurityConfig
extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
protected UserDetailsService userDetailsService() {
return super.userDetailsService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password("$2a$10$WtukGtXlkDrqDrH.0pUdXOi6NBN0exY8o6Upj3E1d4IK4hgT3fPuO")
.roles("admin")
.and()
.withUser("iscolt")
.password("$2a$10$WtukGtXlkDrqDrH.0pUdXOi6NBN0exY8o6Upj3E1d4IK4hgT3fPuO")
.roles("user");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/oauth/**").authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.and().csrf().disable();
}
}
测试验证
/**
* This is Description
*
* @author iscolt
* @date 2019/07/12
*/
@RestController
public class HelloController {
@GetMapping("/admin/hello")
public String admin() {
return "hello admin";
}
@GetMapping("/user/hello")
public String user() {
return "hello user";
}
@GetMapping("/hello")
public String hello() {
return "hello";
}
}