Spring Security Oauth2 架构
1. 用户访问,此时没有Token。Oauth2RestTemplate会报错,这个报错信息会被
Oauth2ClientContextFilter捕获并重定向到认证服务器
2. 认证服务器通过Authorization Endpoint进行授权,并通过AuthorizationServerTokenServices生成授权码并返回给客户端
3. 客户端拿到授权码去认证服务器通过Token Endpoint调用AuthorizationServerTokenServices生成Token并返回给客户端
4. 客户端拿到Token去资源服务器访问资源,一般会通过Oauth2AuthenticationManager调用
ResourceServerTokenServices进行校验。校验通过可以获取资源。
Spring Security Oauth2 授权码模式
添加依赖
<?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.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shsxt</groupId>
<artifactId>spring-security-oauth2-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-security-oauth2-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-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-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- commons-pool2对象池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
编写实体类
/**
* @author yiming
* @description
*/
public class User implements UserDetails {
private String username;
private String password;
private List<GrantedAuthority> authorities;
public User(String username, String password, List<GrantedAuthority> authorities) {
this.username = username;
this.password = password;
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
编写Service
/**
* @author yiming
* @description 自定义登录逻辑
*/
@Service
public class UserService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String password = passwordEncoder.encode("123456");
return new User(username, password,
AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}
编写 Controller
/**
* @author yiming
* @description
*/
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/getCurrentUser")
public Object getCurrentUser(Authentication authorization) {
return authorization.getPrincipal();
}
}
编写配置类
SecurityConfig.java
/**
* @author yiming
* @description
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
// 放行oauth端点
.antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
AuthorizationServerConfig.java
/**
* @author yiming
* @description 配置授权服务器
*/
@Configuration
@EnableAuthorizationServer // 开启授权服务器
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserService userService;
@Autowired
@Qualifier("redisTokenStore")
private TokenStore tokenStore;
/**
* 授权码模式
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 客户端放在内存中
clients.inMemory()
// 客户端id
//配置client_id
.withClient("client")
// 密钥
//配置client-secret
.secret(passwordEncoder.encode("112233"))
//配置访问token的有效期
//.accessTokenValiditySeconds(3600)
//配置刷新token的有效期
//.refreshTokenValiditySeconds(864000)
// 重定向地址: 拿授权码
//配置redirect_uri,用于授权成功后跳转
.redirectUris("https://www.baidu.com")
//配置申请的权限范围
// 授权范围
.scopes("all")
// 授权类型
// authorization_code 授权码模式
// password 密码模式
//配置grant_type,表示授权类型
.authorizedGrantTypes("authorization_code", "password");
}
}
ResourceServerConfig.java
/**
* @author yiming
* @description 配置资源服务器
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.requestMatchers().antMatchers("/user/**");
}
}