Oauth2 Authorization_code 模式是 Oauth2 认证中最完善的认证方式。其完整的认证模式如下:
以上就是Oauth2 code 认证的基本方式!
在SpringSecurtiy中 Oauth2 code认证过程基本上符合上面所说的Oauth2 authorization_code 协议的认证过程。SpringSecurity中整合了Oauth2协议,由于SpringSecurity的本质是一系列的过滤器链组成。所以在加入Oauth2协议以后,相当于在SpringSecurity的过滤器链中,加上Oauth2认证的过滤链实现认证服务。
org.springframework.boot
spring-boot-starter-parent
2.0.5.RELEASE
UTF-8
UTF-8
1.8
Finchley.SR1
1.3.2
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
org.springframework.security.oauth
spring-security-oauth2
2.3.3.RELEASE
org.springframework.boot
spring-boot-starter-test
test
org.springframework.security
spring-security-test
test
mysql
mysql-connector-java
runtime
org.mybatis.spring.boot
mybatis-spring-boot-starter
${mybatis.version}
org.springframework.boot
spring-boot-starter-jdbc
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
PasswordEncoder passwordEncoder;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests()
.antMatchers("/oauth/**","/login")
.permitAll()
.anyRequest()
.authenticated()
.and().csrf().disable();
}
@Bean
@Override
protected UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("hello1").password(passwordEncoder.encode("123456")).authorities("USER").build());
manager.createUser(User.withUsername("hello2").password(passwordEncoder.encode("123456")).authorities("USER").build());
return manager;
}
}
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.antMatchers("/oauth/**")
.permitAll()
.anyRequest()
.authenticated();
}
}
@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.realm("oauth2-resources")
//url:/oauth/token_key,exposes public key for token verification if using JWT tokens
.tokenKeyAccess("permitAll()")
//url:/oauth/check_token allow check token
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.secret(passwordEncoder.encode("secret"))
.redirectUris("http://example.com")
.authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token",
"password", "implicit")
.scopes("all")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(1200)
.refreshTokenValiditySeconds(50000);
}
}
整个认证服务器和资源服务器的代码基本上已经实现。下面我们进行测试配合code认证的流程进行说明。
http://localhost:8080/oauth/authorize?response_type=code&client_id=client1&redirect_uri=http://example.com&scop=all
参数说明:
client_id:客户端的ID,必选项
redirect_uri:重定向URI,必选项
scope:申请的权限范围,可选项
state:任意值,认证服务器会原样返回,用于抵制CSRF(跨站请求伪造)攻击。
如果当前用户没有登录的话,会重定向到登录页面,强制用户进行登录认证。
这个步骤相当于:
登录后认证服务器会进行授权,授权通过之后会产生一个code码:
http://example.com/?code=03cQZu
code码只能使用一次。
注意:这里的redirect_uri=http://example.com 必须配置在ClientDetailsServiceConfigurer 中,不然会抛出异常:
{"error":"invalid_grant","error_description":"Redirect URI mismatch."}
curl -i -d "grant_type=authorization_code&code=gnyW4f&client_id=client&client_secret=secret&redirect_uri=http://example.com" -X POST http://localhost:8080/oauth/token
请求参数说明:
client_id:客户端的ID,必选项。
client_secret:客户端的密钥,必选项。
grant_type:表示使用的授权模式,必选项。
refresh_token:表示早前收到的更新令牌,必选项。
redirect_uri:当前版本必选项
返回值:
{
"access_token": "10337f46-fe9b-4914-9f1b-15d3bfd6227b",
"token_type": "bearer",
"refresh_token": "1cc55626-9fc0-47ac-8f3f-685abd11deaf",
"expires_in": 990,
"scope": "all"
}
返回参数说明:
access_token:访问令牌,必选项。
token_type:令牌类型,该值大小写不敏感,必选项。
expires_in:过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
refresh_token:更新令牌,用来获取下一次的访问令牌,可选项。
scope:权限范围,如果与客户端申请的范围一致,此项可省略。
curl -i -X POST -H "Accept: application/json" -u "client:secret" http://localhost:8080/oauth/check_token?token=a388c2fa-2243-4b7a-b8c5-09973cde4fb2
返回值:
{
"aud": [
"oauth2-resource"
],
"user_name": "hello1",
"scope": [
"all"
],
"active": true,
"exp": 1539929197,
"authorities": [
"USER"
],
"client_id": "client"
}
curl -i -d "grant_type=refresh_token&refresh_token=8b6c262d-22cb-4e5b-af08-a32cbee5c5c2" -u "client:secret" -X POST http://localhost:8080/oauth/token
返回结果:
{
"access_token": "3de40530-a9cf-44ce-a0ef-1165a11add1d",
"token_type": "bearer",
"refresh_token": "8b6c262d-22cb-4e5b-af08-a32cbee5c5c2",
"expires_in": 1199,
"scope": "all"
}