Order
The previous article described the client credentials authorization mode of spring security oauth2, which is generally used in authorization scenarios related to open platform api authentication that are not related to users. This article mainly talks about password mode, one of the authorization modes related to users.
Reviewing the Four Models
OAuth 2.0 defines four authorization methods.
- Authorization code
- Simplified model
- Resource owner password credentials
- Client credentials (
It is mainly used for api authentication and has nothing to do with users.
)
maven
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Configuration
security config
Password mode is supported To configure AuthenticationManager
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//需要正常运行的话,需要取消这段注释,原因见下面小节
// @Override
// public void configure(HttpSecurity http) throws Exception {
// http.csrf().disable();
// http.requestMatchers().antMatchers("/oauth/**")
// .and()
// .authorizeRequests()
// .antMatchers("/oauth/**").authenticated();
// }
//配置内存模式的用户
@Bean
@Override
protected UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("demoUser1").password("123456").authorities("USER").build());
manager.createUser(User.withUsername("demoUser2").password("123456").authorities("USER").build());
return manager;
}
/**
* 需要配置这个支持password模式
* support password grant type
* @return
* @throws Exception
*/
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
This is a new configuration than client credentials mode. It mainly configures users and authenticationManager.
Auth server configuration
@Configuration
@EnableAuthorizationServer //提供/oauth/authorize,/oauth/token,/oauth/check_token,/oauth/confirm_access,/oauth/error
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()") //url:/oauth/token_key,exposes public key for token verification if using JWT tokens
.checkTokenAccess("isAuthenticated()") //url:/oauth/check_token allow check token
.allowFormAuthenticationForClients();
}
/**
* 注入authenticationManager
* 来支持 password grant type
*/
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("demoApp")
.secret("demoAppSecret")
.authorizedGrantTypes("client_credentials", "password", "refresh_token")
.scopes("all")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(1200)
.refreshTokenValiditySeconds(50000);
}
Remember to inject authenticationManager here to support password mode
Otherwise, the error is reported as follows
➜ ~ curl -i -X POST -d "username=demoUser1&password=123456&grant_type=password&client_id=demoApp&client_secret=demoAppSecret" http://localhost:8080/oauth/token
HTTP/1.1 400
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
X-Application-Context: application
Cache-Control: no-store
Pragma: no-cache
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 03 Dec 2017 07:01:27 GMT
Connection: close
{"error":"unsupported_grant_type","error_description":"Unsupported grant type: password"}
Resource server configuration
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
// /**
// * 要正常运行,需要反注释掉这段,具体原因见下面分析
// * 这里设置需要token验证的url
// * 这些需要在WebSecurityConfigurerAdapter中排查掉
// * 否则优先进入WebSecurityConfigurerAdapter,进行的是basic auth或表单认证,而不是token认证
// * @param http
// * @throws Exception
// */
// @Override
// public void configure(HttpSecurity http) throws Exception {
// http.requestMatchers().antMatchers("/api/**")
// .and()
// .authorizeRequests()
// .antMatchers("/api/**").authenticated();
// }
}
Verification
Request token
curl -i -X POST -d "username=demoUser1&password=123456&grant_type=password&client_id=demoApp&client_secret=demoAppSecret" http://localhost:8080/oauth/token
Return
HTTP/1.1 200
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
X-Application-Context: application
Cache-Control: no-store
Pragma: no-cache
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 03 Dec 2017 04:53:40 GMT
{"access_token":"4cfb16f9-116c-43cf-a8d4-270e824ce5d7","token_type":"bearer","refresh_token":"8e9bfbda-77e5-4d97-b061-4e319de7eb4a","expires_in":1199,"scope":"all"}
Carry token to access resources
curl -i http://localhost:8080/api/blog/1\?access_token\=4cfb16f9-116c-43cf-a8d4-270e824ce5d7
Or ..
curl -i -H "Accept: application/json" -H "Authorization: Bearer 4cfb16f9-116c-43cf-a8d4-270e824ce5d7" -X GET http://localhost:8080/api/blog/1
Return
HTTP/1.1 302
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Set-Cookie: JSESSIONID=F168A54F0F3C3D96A053DB0CFE129FBF; Path=/; HttpOnly
Location: http://localhost:8080/login
Content-Length: 0
Date: Sun, 03 Dec 2017 05:20:19 GMT
See the next summary for the cause of the error.
Successful return
HTTP/1.1 200
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
X-Application-Context: application
Content-Type: text/plain;charset=UTF-8
Content-Length: 14
Date: Sun, 03 Dec 2017 06:39:24 GMT
this is blog 1
Error return
HTTP/1.1 401
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Cache-Control: no-store
Pragma: no-cache
WWW-Authenticate: Bearer realm="oauth2-resource", error="invalid_token", error_description="Invalid access token: 466ee845-2d08-461b-8f62-8204c47f652"
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 03 Dec 2017 06:39:28 GMT
{"error":"invalid_token","error_description":"Invalid access token: 466ee845-2d08-461b-8f62-8204c47f652"}
WebSecurityConfigurerAdapter and ResourceServerConfigureRadapter
Both have configurations for http security, and their default configurations are as follows
WebSecurityConfigurerAdapter
spring-security-config-4.2.3.RELEASE-sources.jar! /org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java
@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
//......
protected void configure(HttpSecurity http) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
//......
}
You can see that WebSecurityConfigurerAdapter’s order is 100
ResourceServerConfigurerAdapter
spring-security-oauth2-2.0.14.RELEASE-sources.jar! /org/springframework/security/oauth2/config/annotation/web/configuration/ResourceServerConfigurerAdapter.java
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
Its order is securityproperties.access _ override _ order-1
spring-boot-autoconfigure-1.5.5.RELEASE-sources.jar! /org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerProperties.java
/**
* The order of the filter chain used to authenticate tokens. Default puts it after
* the actuator endpoints and before the default HTTP basic filter chain (catchall).
*/
private int filterOrder = SecurityProperties.ACCESS_OVERRIDE_ORDER - 1;
This shows that WebSecurityConfigurerAdapter’s interception has priority over ResourceServerConfigurationAdapter.
The relationship between the two
- WebSecurityConfigurerAdapter is used to protect oauth-related endpoints, and is mainly used for user login (
form login
,Basic auth
) - ResourceServerConfigurationAdapter is used to protect oauth’s resources to be opened, and is mainly used for client-side and token authentication (
Bearer auth
)
Therefore, the two are divided and cooperated.
- The WebSecurityConfigurerAdapter does not block oauth’s resources to be opened.
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestMatchers().antMatchers("/oauth/**")
.and()
.authorizeRequests()
.antMatchers("/oauth/**").authenticated();
}
- Configure the resource that requires token authentication on resourceserverconfigureradapter
@Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/api/**")
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
This is the end of the story.
doc
-
Understanding OAuth 2.0(
good
) -
Secure Spring REST API using OAuth2(
good
) - Handling error: UnsupportedGrantTypeException, Unsupported grant type: password #3
- Spring Oauth2 : Authentication Object was not found in the SecurityContext
- Spring Security OAuth2, which decides security?
- Using WebSecurityConfigurerAdapter with Spring OAuth2 and user-info-uri
- How to define order of spring security filter chain #1024
- Relation between WebSecurityConfigurerAdapter and ResourceServerConfigurerAdapter
- Spring Security OAuth2, which decides security?