Whats are the difference between these classes? I know that WebSecurityConfigurerAdapter is used to customize "security" on our apps.
Whats I've done:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
CustomUserDetailsService customUserDetailsService;
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
But i don't understand the meaning of AuthorizationServerConfigurerAdapter.
I read a couple of articles but I don't get it.
One thing first. OAuth 2 is an authorization framework. It allows an application (client) to obtain limited access to a HTTP service on behalf of a resource owner (user). OAuth 2 is not an authentication protocol.
AuthorizationServerConfigurerAdapter is used to configure how the OAuth authorization server works.
Here are some aspects which can be configured:
supported grant types (e.g. authorization code grant)
authorization code service, to store authorization codes
token store, to store access and refresh tokens (e.g. JwtTokenStore)
client details service, which holds the client configurations
...
WebSecurityConfigurerAdapter is used to configure how the OAuth authorization server is secured.
Or in other words, how the user has to authenticate to grant a client access to his resources.
This can be:
form authentication
authentication via an identity provider (Facebook Login)
...
(I have intentionally omitted some details to keep the answer as simple as possible.)
Example authorization server configuration with an in-memory token store:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore());
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
...
}
Example security configuration with form login:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/oauth/authorize").authenticated()
.and()
.formLogin();
}
...
}
if you want to use a third party authenticater, this means at the same time OAuth, then you must use AuthorizationServerConfigurerAdapter with WebSecurityConfigurerAdapter at OAuth serverside. If not so, WebSecurityConfigurerAdapter is enough for ordinary authentication
Related
Moin!
I use Spring Security 5 with Kerberos for the SSO authentication in my project.
In the WebSecurityConfig I register two AuthenticationProvider
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(msfUserDetailsService).passwordEncoder(passwordEncoder());
assertThatUnlimitedCryptographyEnabled();
// Two providers
auth.authenticationProvider(kerberosAuthenticationProvider());
auth.authenticationProvider(kerberosServiceAuthenticationProvider());
}
This seems to be the way its done as seen in these two examples:
https://docs.spring.io/spring-security-kerberos/docs/current/reference/html/ssk-spnego.html
https://www.ebayinc.com/stories/blogs/tech/customizing-spring-security-with-multiple-authentications/
However I don't see why I need both of them. During authentication the KerberosServiceAuthenticationProvider is the one that is validating Kerberos tickets (see JavaDoc)
However what is the KerberosAuthenticationProvider for? The JavaDoc in this case just says
AuthenticationProvider for kerberos.
As you said: KerberosServiceAuthenticationProvider is used to validate tickets in SSO authentication, whereas KerberosAuthenticationProvider is used for form based authentications, which are usually used as a fallback when SSO is not supported on client side (e.g. browsers on linux systems). This type of authentication is handled by UsernamePasswordAuthenticationFilter which is applied in WebSecurityConfigurerAdapter:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
...
.formLogin()
...
}
...
}
If you do not use form login then you can omit this provider as pointed in comments.
I have a spring-security-oauth2 project running smoothly with a class as Authorization server.
The client-ids, user-tokens, refresh-tokens are all managed by the database.
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static String REALM = "MY_OAUTH_REALM";
...
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.realm(REALM + "/client");
}
}
Everything is working fine except that i don't have any idea what the configure method is doing. Even if i remove the complete method the oauth2 process still works fine.
What is the main use of configure method in this context and what realm is it setting here?
Please help me in understanding it.
Thanks.
Purpose of configure method
AuthorizationServerConfigurerAdapter has three configure(...) methods and all three could be overridden and those serve different purposes.
In your question, you have quoted only one.
Their purpose is to provide your custom settings for Authorization Server end points, clients & security. So its up to you as how many you wish to override as there are some predefined default settings.
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// This can be used to configure security of your authorization server itself
// i.e. which user can generate tokens , changing default realm etc.
// Sample code below.
// We're allowing access to the token only for clients with 'ROLE_TRUSTED_CLIENT' authority.
// There are few more configurations and changing default realm is one of those
oauthServer
.tokenKeyAccess("hasAuthority('ROLE_TRUSTED_CLIENT')")
.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// Here you will specify about `ClientDetailsService`
// i.e. information about OAuth2 clients & where their info is located - memory , DB , LDAP etc.
// Sample code below.
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// This can be used to configure security of your authorization server itself
// i.e. which user can generate tokens , changing default realm etc - Sample code below.
// we're allowing access to the token only for clients with 'ROLE_TRUSTED_CLIENT' authority.
// There are few more configurations and changing default realm is one of those
oauthServer
.tokenKeyAccess("hasAuthority('ROLE_TRUSTED_CLIENT')")
.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// Here you will specify about `ClientDetailsService` i.e.
// information about OAuth2 clients & where their info is located - memory , DB , LDAP etc.
// Sample code below
clients.inMemory()
.withClient("trusted-app")
.authorizedGrantTypes("client_credentials", "password", "refresh_token")
.authorities("ROLE_TRUSTED_CLIENT")
.scopes("read", "write")
.resourceIds("oauth2_id")
.accessTokenValiditySeconds(10000)
.refreshTokenValiditySeconds(20000)
.secret("secret");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// Here you will do non-security configs for end points associated with your Authorization Server
// and can specify details about authentication manager, token generation etc. Sample code below
endpoints
.authenticationManager(this.authenticationManager)
.tokenServices(tokenServices())
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter());
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("abcd");
return converter;
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenEnhancer(accessTokenConverter());
return defaultTokenServices;
}
Purpose of #EnableAuthorizationServer
A javadoc explanation is already provided in previous answer.
In layman's language, this is to set up your token generation end point i.e.
if you provide the properties security.oauth2.client.client-id and security.oauth2.client.client-secret, Spring will give you an authentication server, providing standard Oauth2 tokens at the endpoint /oauth/token
In practical scenario, what this means is that you are setting up a token generation web-application ( Layer-7 ) on top of your enterprise User LDAP or User Database and is usually a separate application from your consumer side apps ( APIs etc ).
If you have a look at the JavaDoc comment for #EnableAuthorizationServer you can see that it says the following;
Convenience annotation for enabling an Authorization Server (i.e. an
AuthorizationEndpoint and a TokenEndpoint in the current application
context, which must be a DispatcherServlet context. Many features of
the server can be customized using #Beans of type
AuthorizationServerConfigurer (e.g. by extending
AuthorizationServerConfigurerAdapter. The user is responsible for
securing the Authorization Endpoint (/oauth/authorize) using normal
Spring Security features (EnableWebSecurity #EnableWebSecurity
etc.), but the Token Endpoint (/oauth/token) will be automatically
secured using HTTP Basic authentication on the client's credentials.
Clients must be registered by providing a ClientDetailsService through
one or more AuthorizationServerConfigurers.
Extending AuthorizationServerConfigurerAdapter is just used for customization of the Authorization Server. You can easily set up a functioning Authorization Server within Spring Security by Just Annotating a Bean class with #EnableAuthorizationServer
I have a web service that I have built on top of Spring. I am currently authenticating using Spring Security as follows:
#Configuration
#EnableGlobalMethodSecurity(securedEnabled=true)
#EnableWebSecurity
public class ServerSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Properties properties;
private static final String ALL_URI = "/v1/**";
private static final String HEALTH_URI = "/v1/healthCheck";
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(getFilter(), BasicAuthenticationFilter.class);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers(HEALTH_URI).permitAll()
.anyRequest().authenticated();
http.csrf().disable();
}
private AuthenticationFilter getFilter() {
return new AuthenticationFilter( properties.getKey());
}
}
My AuthenticationFilter class extends AbstractAuthenticationProcessingFilter and performs the actual authentication. If I want to add Authorization to my security, would I just make those checks in the attemptAuthentication method apart of the AuthenticationFilter? Or is there a better way to do it? The way I understand it is that Authorization and Authentication should be done independently. You first authenticate, and then you verify the permissions. So, I would assume there would be a better approach to do authorization within Spring Security rather than just adding it to the attemptAuthentication method.
You need a AuthenticationProvider to do authenticate, implement the AuthenticationProvider and override the authentication and supports methods, and then inject to the AuthenticationManager.
attemptAuthentication method in filter is usually to get authentication(e.g. UsernamePasswordFilter gets username and password from request, and then builds a UsernamePasswordAuthenticationToken to AuthenticationManager ),
supports method tests the AuthenticationProvider whether can be used to do authenticate.(e.g DaoAuthenticationProvider supports UsernamePasswordAuthenticationToken)
authenticate method is used to do authenticate(e.g DaoAuthenticationProvider gets the real password by username and then compare to the user input), this method should return an Authentication that is already authenticated(e.g UsernamePasswordAuthenticationToken), and this authentication should contains the user authorities(this can be used to hasRole('xxx')), or use detail and so on.
After attemptAuthentication successful, the Authentication will set into SecurityContextHolder. and then you can use the hasRole('xx'), or something else.
I'm using Spring Boot and I want my app to host Oauth2 resource server for accessing my api endpoints on the same server. I also need to have a web interface with secured pages via form login.
For example I have api endpoints /api/v1/** where requests can only be made via having a token from my oauth2 resource server.
Additionally there are endpoints like /account/** where user needs to be logged in via form.
All of this needs to be in one Spring Boot instance for now.
My WebSecurityConfig file:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/account/**").authenticated()
.and()
.httpBasic();
}
}
And in my Oauth2SecurityConfig I have:
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/v1/me/**").authenticated();
}
}
The problem is, oauth2 config seems to override the first configuration and all my webpage resources are exposed without asking username password in form login. And if I try accessing my api endpoints I get the expected oauth error response.
Do I need to have them both in one overriden method? Do I need to have 2 instances of HttpSecurity? How can I solve this?
I had faced the same situation. Finally got a solution for this. You just need to make the use of #Order annotation.
Add this to your WebSecurityConfig class
#Order(1)
And add this to your ResourceServerConfiguration class
#Order(2)
To secure all your resources you would have to add
.authorizeRequests().anyRequest().authenticated(). Did you mean that your "/account/**" resource is exposed?
Those two HttpSecurity objects are not the same object. That being said you don't need to have both of them configured, but each one of them serves different purpose. (I have both of them configured in my project and it's working fine)
I would start by reviewing your expectations for this as I am not sure whether it is feasible to have a part of your server protected by OAuth and another part by another authentication mechanism (Form login). Both would be considered .authenticated(). You could manage the access by roles and oauth scopes, but then you would have to also provide both login/logout mechanisms by different filters and maybe another filter to provide persisting of both authentications when navigating through your server. Seems to me like a lot of not so standard work and I would think about different solutions.
I am just a beginner in Spring Security Oauth2.
I try to make Authorization Server and Resource Server (separated and connect to JDBC) and the purpose is to make Single Sign-On.
My flow success to get accesstoken and refreshtoken from Authorization Server. My accesstoken always used as parameter to access Resouce Server, and this is give a response back.
for example http://127.0.0.1:8080/Resource/res/staff?access_token=xxxxxxxxxxxxxxxxxxxxx
My problem, if the accesstoken expired, the spring security will prevent to access the page and give the error exception. When I must use the refreshtoken to get new token?
Or is my flow wrong? Is there other flow to renew accesstoken?
Thanks
Edited:
FYI:
I want to make SSO using Spring Security Oauth2. I have several Apps Server (use Spring Framework) and I want to make one server that responsible to manage the login. And I want to make the Apps Server become Resource Server (also the Client)
So I make one Authorization Server with Spring Security Oauth2. The user who wants to access the protected Resource Server must login to Authorization Server (the Resource Server authorize to Authorization Server). It will get a code and then the Resource Server will exchange this code with accessToken and refreshToken. This flow is success.
I can also request the new accessToken using the refreshToken that given by Authorization Server.
But I cannot call this procedure because if I access the url mapping, previously the spring security was blocking the access and give the invalid token error return.
How can I solve the missing link?
Updated:
This is my Authorization Server Configuration:
#Configuration
public class Oauth2AuthorizationServer {
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
DataSource dataSource;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(new JdbcTokenStore(dataSource))
.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("isAnonymous() || permitAll()").checkTokenAccess("permitAll()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.jdbc(dataSource);
}
}
}
And this is my Resource Server Configuration (as Client too)
#Configuration
public class Oauth2ResourceServer {
private static final String RESOURCE_ID = "test";
#Configuration #Order(10)
protected static class NonOauthResources extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/halo").permitAll()
.antMatchers("/api/state/**").permitAll()
.antMatchers("/**").permitAll()
.and().anonymous();
}
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setClientId("jsclient");
tokenService.setClientSecret("jspasswd");
tokenService.setCheckTokenEndpointUrl("http://localhost:8084/Server2Auth/oauth/check_token");
resources
.resourceId(RESOURCE_ID)
.tokenServices(tokenService);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.filterSecurityInterceptorOncePerRequest(true)
.antMatchers("/res/staff").hasRole("STAFF")
.antMatchers("/res/client").access("#oauth2.hasScope('trust')")
.antMatchers("/res/admin").hasRole("ADMIN")
.and()
.exceptionHandling().accessDeniedPage("/403");
}
}
}
Resource (as Client too) request to authorize:
curl -X POST -vu clientauthcode:123456 http://localhost:10000/auth-server/oauth/token -d "client_id=clientauthcode&grant_type=refresh_token&refresh_token=436761f1-2f26-412b-ab0f-bbf2cd7459c4"
Feedback from Authorize Server:
http://localhost:10001/resource-server/api/state/new?code=8OppiR
Resource (as Client) exchange the code to Authorize Server:
curl -X POST -vu clientauthcode:123456 http://localhost:10000/auth-server/oauth/token -H "Accept: application/json" -d "grant_type=authorization_code&code=iMAtdP&redirect_uri=http://localhost:10001/resource-server/api/state/new"
Feedback from Authorize Server:
{
"access_token":"08664d93-41e3-473c-b5d2-f2b30afe7053",
"token_type":"bearer",
"refresh_token":"436761f1-2f26-412b-ab0f-bbf2cd7459c4",
"expires_in":43199,
"scope":"write read"
}
Resource (as Client) access the url itself
curl http://localhost:10001/resource-server/api/admin?access_token=08664d93-41e3-473c-b5d2-f2b30afe7053
Request new access toke using refresh token
curl -X POST -vu clientauthcode:123456 http://localhost:10000/auth-server/oauth/token -d "client_id=clientauthcode&grant_type=refresh_token&refresh_token=436761f1-2f26-412b-ab0f-bbf2cd7459c4"
The OAuth2 Spec has a section on refreshing access tokens. It's implemented in a pretty standard way in Spring OAuth (you just post the refresh token to the /token endpoint).
BTW for SSO you don't normally need a Resource Server. But that's a different question.