Spring Security OAuth2的基本使用
Spring Security OAuth2
Spring Security OAuth2对Oauth2进行了实现,主要包含认证服务器和资源服务器两大块的实现。
认证服务器
对四种授权模式的实现和Token的生成与存储,也可自定义获取Token的方式
资源服务器
在Spring Security过滤器链上加OAuth2AuthenticationProcessingFilter过滤器,让OAuth2协议发放令牌认证的方式来保护资源
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置认证服务器
自定义MyUser对象
@Data
public class MyUser implements Serializable {
private String userName;
private String password;
private boolean accountNonExpired = true;
private boolean accountNonLocked = true;
private boolean credentialsNonExpired = true;
private boolean enabled = true;
}
创建认证服务器
在Spring Security的配置类上使用@EnableAuthorizationServer注解申明
@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
自定义MyUserDetailService
自定义MyUserDetailService,实现账号登录相关校验逻辑,预设密码必须为123456,且拥有admin权限,使用任意账号即可登录。
@Service
public class MyUserDetailService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MyUser user = new MyUser();
user.setUserName(username);
user.setPassword(this.passwordEncoder.encode("123456"));
return new User(username, user.getPassword(), user.isEnabled(),
user.isAccountNonExpired(), user.isCredentialsNonExpired(),
user.isAccountNonLocked(), AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}
配置application.yml
若不配置指定client-id和client-secret,则会随机分配
security:
oauth2:
client:
client-id: web
client-secret: 123456789
registered-redirect-uri: http://127.0.0.1:8888/test
启动项目,控制台打印出随机分配的client-id和client-secret
security.oauth2.client.client-id = 798f97b8-e9b0-4c87-88c2-6b35b9813966
security.oauth2.client.client-secret = 782fa08a-bec5-4d27-8269-7f7b35488143
若配置指定,则打印指定的参数
security.oauth2.client.client-id = web
security.oauth2.client.client-secret = ****
授权码模式获取令牌
向认证服务器请求授权码
http://localhost:8888/oauth/authorize?response_type=code&client_id=web&redirect_uri=http://127.0.0.1:8888/test&scope=all
response_type=code:表示授权码模式
client_id=web:配置文件中指定的web
redirect_uri:指定一个地址,用来重定向获取授权码
scope=all:表示所有权限
根据定义的UserDetailService逻辑,使用任意用户名,密码123456登录即可  注意:指定的redirect_uri必须同时在配置文件中指定,否则出现以下错误提示
 登录成功后页面成功跳转到授权页面  选择Approve,点击Authorize按钮后,页面跳转到指定的redirect_uri,并带上授权码信息  通过授权码CODE从认证服务器获取令牌Token
grant_type:authorization_code
code:授权码
client_id:web
redirect_uri:http://127.0.0.1:8888/test
scope:all

请求头添加:key=Authorization,value=Basic加上client_id:client_secret 经过base64加密后的值
base64:https://1024tools.com/base64
  确认参数无误后,发送请求,获取令牌Token
{
"access_token": "882698ab-7a08-4aaf-847c-11c2d191540d",
"token_type": "bearer",
"refresh_token": "ea709f2f-987a-4ecf-ae79-62267a453a10",
"expires_in": 43199,
"scope": "all"
}
注意:一个授权码只能换一次令牌,如果再次使用相同授权码Code获取令牌,将返回如下信息
{
"error": "invalid_grant",
"error_description": "Invalid authorization code: E6Het7"
}
密码模式获取令牌
密码模式获取令牌相对简单 ,直接发送Post请求:http://localhost:8888/oauth/token  请求头添加:key=Authorization,value=Basic加上client_id:client_secret经过base64加密后的值  发送请求,获取令牌
{
"access_token": "5983f2eb-d83d-4d5a-8e94-89cd417d360f",
"token_type": "bearer",
"refresh_token": "56b1d244-d5d5-4f3c-8597-7921f6d6c03b",
"expires_in": 43199,
"scope": "all"
}
配置资源服务器
配置资源服务器,让客户端可以通过合法的令牌来获取定义的资源。
若不配置资源服务器,直接使用Token(请求头添加key=Authorization,值=token_type access_token)访问资源,即使Token正确,也无法访问资源信息。将返回如下信息:
{
"timestamp": "2022-10-20T07:06:26.045+00:00",
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/index"
}
定义资源
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("index")
public Object index(Authentication authentication) {
return authentication;
}
}
配置application.yml
user-info-uri 指定认证服务器地址和端口
security:
oauth2:
resource:
user-info-uri: http://127.0.0.1:8888
配置资源服务器
在配置类上使用@EnableResourceServer注解申明是一个资源服务器
@Configuration
@EnableResourceServer
public class MyResourceServerConfig extends ResourceServerConfigurerAdapter{
}
执行测试
使用授权模式或者密码模式获取令牌,然后使用令牌请求资源地址:http://localhost:8888/index ,注意携带请求头。
 使用错误access_token
{
"error": "invalid_token",
"error_description": "Invalid access token: 1d4379313-c2f3-4ba4-87ce-26371726ba62"
}
使用正确access_token
{
"authorities": [
{
"authority": "admin"
}
],
"details": {
"remoteAddress": "127.0.0.1",
"sessionId": null,
"tokenValue": "d4379313-c2f3-4ba4-87ce-26371726ba62",
"tokenType": "Bearer",
"decodedDetails": null
},
"authenticated": true,
"userAuthentication": {
"authorities": [
{
"authority": "admin"
}
],
"details": {
"grant_type": "password",
"username": "admin",
"scope": "all"
},
"authenticated": true,
"principal": {
"password": null,
"username": "admin",
"authorities": [
{
"authority": "admin"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": null,
"name": "admin"
},
"principal": {
"password": null,
"username": "admin",
"authorities": [
{
"authority": "admin"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"oauth2Request": {
"clientId": "web",
"scope": [
"all"
],
"requestParameters": {
"grant_type": "password",
"username": "admin",
"scope": "all"
},
"resourceIds": [],
"authorities": [
{
"authority": "ROLE_USER"
}
],
"approved": true,
"refresh": false,
"redirectUri": null,
"responseTypes": [],
"extensions": {},
"grantType": "password",
"refreshTokenRequest": null
},
"clientOnly": false,
"credentials": "",
"name": "admin"
}
注意事项
若同时定义认证服务器和资源服务器,使用授权码模式获取令牌可能会遇到异常:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
解决方案:使用@Order() 注解,只要确保认证服务器先于资源服务器配置即可
@Order(1)
@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@Order(2)
@Configuration
@EnableResourceServer
public class MyResourceServerConfig extends ResourceServerConfigurerAdapter {
}
|