这篇文章我们来简单介绍下如何使用oauth的授权接口。oauth2为我们提供了认证服务和资源服务,认证服务需要引入@EnableAuthorizationServer来开启,资源服务需要引入 @EnableResourceServer来开启。 oauth2的授权接口是oauth/token,通过TokenEndpoint暴露该接口,而TokenEndpoint是通过注解@EnableAuthorizationServer引入的。下面我们通过一个简单的示例来了解一下oauth2的认证过程
1、引入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.2.4.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-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2、创建Oauth2Config类,设置oauth的资源服务和认证服务相关配置。
@Configuration
public class Oauth2Config {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 开启oauth2身份认证
*/
@Configuration
@EnableAuthorizationServer
public static class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter{
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//设置用户基本信息,比如scope、granttype、clientid、secret等
clients.inMemory()
.withClient("client_1")
.secret(passwordEncoder.encode("secret_1"))
.scopes("select").authorities("aut")
.authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token", "password")
.redirectUris("http://www.baidu.com");
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.passwordEncoder(passwordEncoder)
//开启表单认证。创建ClientCredentialsTokenEndpointFilter对请求auth/token拦截,并获取client_id和secret进行身份认证
.allowFormAuthenticationForClients();
}
}
/**
* 对所有的请求进行身份拦截验证,除了oauth开头的请求,资源服务配置类会为我们创建OAuth2AuthenticationProcessingFilter
* 该拦截器会获取请求头Authorization并进行相关校验
*/
@Component
@EnableResourceServer
public static class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
}
}
}
3、创建rest接口,用于请求测试。
@RestController
public class IndexController {
@RequestMapping("api/index/{id}")
public String index(@PathVariable String id) {
return id;
}
}
4、启动服务。我们先访问接口http://127.0.0.1:8080/api/index/1看请求是否被拦截。
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
直接访问的话,会返回认证错误的提示。
5、获取票据。我们可以通过oauth/token接口获取票据,通过grant_type参数指定获取token的授权类型,取值有client_credentials、password、authorization_code、refresh_token。
5.1、client_credentials
grant_type:授权类型,用户client_1必须有相对应的授权权限
scope:作用域,用户client_1必须有相对应的scope
client_id:用户id
client_secret:用户密码
这时候我们在请求api的时候在请求头中添加Authorization,值为Bearer 87772dc0-2906-492f-bc73-c69e42d66605,Bearer后面的值就是我们刚刚获取的access_token。expires_in单位为秒,再次访问api,访问成功。
5.2、password。如果grant_type为密码,我们需要再配置AuthenticationManager和UserDetailsService
5.2.1、创建AuthenticationManager。新建类SecurityConfig,继承WebSecurityConfigurerAdapter,重写authenticationManager(),将该方法定义为一个bean
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
5.2.2、创建UserDetailsService。新建类CustomUserService,继承UserDetailsService
@Component
public class CustomUserService implements UserDetailsService {
@Resource
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User(username, passwordEncoder.encode(username), AuthorityUtils.createAuthorityList("user"));
}
}
5.2.2、在oauth的认证服务中配置AuthenticationManager和UserDetailsService。
/**
* 开启oauth2身份认证
*/
@Configuration
@EnableAuthorizationServer
public static class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter{
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
super.configure(clients);
clients.inMemory()
.withClient("client_1")
.secret(passwordEncoder.encode("secret_1"))
.scopes("select").authorities("aut")
.authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token", "password");
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.passwordEncoder(passwordEncoder)
//开启表单认证。创建ClientCredentialsTokenEndpointFilter对请求auth/token拦截,并获取client_id和secret进行身份认证
.allowFormAuthenticationForClients();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService);
}
}
5.2.3、访问oauth/token
和client_credentials相比,grant_type的值改为了password,同时也增加了username和password两个参数。
5.3、refresh_token
和client_credentials相比,grant_type的值改为了refresh_token,同时也增加了refresh_token参数。