I am trying to implement Oauth2 in my existing application.Initially I have added spring security and then tried to add oauth2, After adding configuration I am able to generate access_token but by using access_token i am not able to access resources.
Here is my code:
SecurityConfiguration.java
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
private ClientDetailsService clientDetailsService;
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/patients").permitAll()
.antMatchers("/oauth/token").permitAll()
.anyRequest().authenticated()
.and().httpBasic();
http.csrf().disable();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username, password, 1 as enabled from user where username=?")
.authoritiesByUsernameQuery("select username, authority from authorities where username=?");
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public JdbcTokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Bean
@Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore) {
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
@Bean
@Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
SecurityOAuth2Configuration.java
@Configuration
@EnableAuthorizationServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Import(SecurityConfiguration.class)
public class SecurityOAuth2Configuration extends AuthorizationServerConfigurerAdapter {
private static String REALM = "CRM_REALM";
private static final int ONE_DAY = 60 * 60 * 24;
private static final int THIRTY_DAYS = 60 * 60 * 24 * 30;
@Autowired
private TokenStore tokenStore;
@Autowired
private DataSource dataSource;
@Autowired
private UserApprovalHandler userApprovalHandler;
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.realm(REALM);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager);
}
}
ResourceServer.java
@Configuration
@EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.anonymous().disable()
.requestMatchers().antMatchers("/patients/**").and().authorizeRequests()
.antMatchers("/patient/**").access("hasRole('USER')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
I have used this tutorial for reference.
I am able to get access token using basic auth credentials.
But when i used the same access token to get resources, it is failing.
I have added all required tables for oauth. Is there anything am i missing?
Update:
I removed .and().httpBasic();
and
added @Order(3) in WebsecurityConfigurerAdapter and updated properties file with security.oauth2.resource.filter-order = 3
now getting error as
{
"timestamp": 1543500350487,
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/patient/1/"
}
Update 2
here is my user and authorities schema:
user
+----------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-----------------+------+-----+---------+----------------+
| id | int(6) unsigned | NO | PRI | NULL | auto_increment |
| username | varchar(50) | NO | UNI | NULL | |
| password | varchar(100) | NO | | NULL | |
+----------+-----------------+------+-----+---------+----------------+
authorities
+-----------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-----------------+------+-----+---------+----------------+
| id | int(6) unsigned | NO | PRI | NULL | auto_increment |
| username | varchar(50) | NO | MUL | NULL | |
| authority | varchar(50) | NO | | NULL | |
+-----------+-----------------+------+-----+---------+----------------+
You should use hasRole
directly on your antmatcher instead of a string inside the access()
function. This will evaluate the hasRole
correctly and correctly determine that the user has access to the requested resource.
This will result in the following code for ResourceServer.java
:
@Configuration
@EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.anonymous().disable()
.requestMatchers().antMatchers("/patients/**").and().authorizeRequests()
.antMatchers("/patient/**").hasRole('USER')
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
First off, you have two similar methods that modify the AuthenticationManagerBuilder
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
and
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
Is there a reason they both are there? I do not have this in my config set up.
Furthermore, your query might not be working properly. You should follow some guidelines into how to set up a user service to handle the loaduserbyusername call and the auth
object with one. As a note: I do not have the same AuthenticationManagerBuilder
set up as you, I have mine configured to use a userdetails service, along with a password Encoder like so.
auth.userDetailsService(securityUserService)
.passwordEncoder(passwordEncoders.userPasswordEncoder());
If that doesn't help, here's an alternative way of configuring:
Change the class that extends WebSecurityConfigurerAdapter
to only concern itself with the token endpoint.
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/oauth/**").permitAll()
.and()
.csrf()
.disable();
}
Now in your ResourceServerConfigurerAdapter
, have the configuration worry about the stuff in the resource server. Please note, this will work only if your AuthenticationManagerBuilder
configuration is properly loading the Role correctly. As other's have noted, Spring has the prefix ROLE_
. Which for some reason you are retrieving using a query, and they're authorities.
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.requestMatchers()
.antMatchers("/api/**")
.and()
.authorizeRequests()
.antMatchers("/api/**").access("hasRole('USER')")
.and()
.exceptionHandling()
.accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
In my AuthServerConfig
file I Do not have the following annotations:
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Import(SecurityConfiguration.class)
I configure the AuthorizationServerSecurityConfigurer
differently than the tutorial you followed, mine is the following:
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
My ClientDetailsServiceConfigurer
is still in memory so that's different as well. My AuthorizationServerEndpointsConfigurer
is slightly different too, I only add a tokenstore, an enhancer chain (Don't worry about this, it's extra), and an authenticationManager
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With