Spring Security - Role check fails permanently [duplicate] - java

This question already has answers here:
Springboot Security hasRole not working
(3 answers)
Closed 1 year ago.
I am trying to learn Spring Security and want to secure an API. I want a login form with database authentication on the one hand, and on the other hand a OAuth2 authentication.
But I am stuck at the first task. The problem is that the application doesn't accept my roles and I have no idea why.
Here's my Config class:
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("SELECT username,password,enabled FROM users WHERE username=?")
.authoritiesByUsernameQuery("SELECT username, authority FROM authorities WHERE username=?");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/createTrack").hasRole("CREATOR")
.antMatchers("/getTrack").hasAnyRole("CREATOR", "USER")
.antMatchers("/test").hasRole("CREATOR")
.antMatchers("/all").permitAll()
.and().formLogin();
}
#Bean
public PasswordEncoder getPasswordEncoder() {
// TODO: Change this to hashed password (this is for demo purposes)
return NoOpPasswordEncoder.getInstance();
}
}
Everything is made like descriped in the Spring documentation: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#servlet-authentication-jdbc
I worked out a few endpoints of my API to test the logged in user's name and role. Everything works fine - the login seems to work great, also the role is correct.
But when I try to call an endpoint that's only accessible for one or both specific roles, it gets me a 403 error response.
What am I doing wrong? I tried a lot of things and it still does not work.
Thanks!
PS: I am using MyBatis (task from my company) - if that matters in any way?

Thank you guys for your help!
Toerktumlare's answer brought me to the solution. I turned on Debug Logging as you suggested. CORS was not the problem, but reading the debug messages brought me on the right way.
The problem was minor though. I stored the roles in the database as "CREATOR" or "USER". Debug messages showed me that Spring was looking for "ROLE_CREATOR" or "ROLE_USER" and - because my roles weren't saved this way - didn't find them. Hence I got a 403 HTTP response.

Related

Spring 5, get info from LDAP (AD)

I'm trying to make a controller that receives an email and fetches information from that user in an Active Directory returning it as json.
I'm having a hard time finding useful material because everything I find is trying to teach authentication through WebSecurity annotation...
I couldn't care less about the authentication, I only want Spring to get info and nothing else.
Can anybody tell me how to get out from this to what I need?
#Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(LDAP_DOMAIN, LDAP_URL, LDAP_ROOT_DN);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setSearchFilter(LDAP_FILTER);
return provider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin();
}
You can use LdapTemplate if you want to just get information. You can find information spring documentation . Besides, this tutorial has lots of ldap query examples with LdapTemplate

Does Spring security JDBC authentication hit the Data base on every request

I wrote one program using spring security framework and used JDBC approach using DataSource. Following is piece of code.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/users/**").hasRole("USER")
.antMatchers(HttpMethod.POST, "/users/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PUT, "/users/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PATCH, "/users/**").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/users/**").hasRole("ADMIN")
.and().httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
}
This program is using default table what spring framework expect.
Now my question, because here I am using httpBasic authentication approach when I will come GET /users url, does spring framework hit the table on every request and valid user with credential or after first authentication it cache and validate against that cache.
Could someone help me to understand it.
Thanks in advance
If you use stateless HTTP Basic, the database will by default be "hit" on every request. If you find that a problem you can set a cache like this:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource).userCache(userCache);
}
See UserCache javadoc for details about which cache implementations you can use.
Because you're using httpBasic(), then the query would be performed each time - this is the nature of stateless authentication, which is what folks using httpBasic() are typically driving at.
You could cache the JDBC query results with an L2 cache or similar.
Or, if you are okay with some authentication state, then you could
add session management, as another answer indicates. Doing this means that the first request would include credentials in the Authorization header and its response would include a session cookie. Subsequent requests would send that session cookie back, instead of the Authorization header, until the session expires.
use a token (like an OAuth token). You can instead present your credentials to an Authorization Server. The Authorization Server will exchange this for a token that you can then supply to the Authorization header (Authorization: Bearer) instead of the user's credentials (Authorization: Basic).
This behaviour is configured by sessionManagement().sessionCreationPolicy() when configuring HttpSecurity.
Unless you set it to sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) ,its default value is IF_REQUIRED which will cache the authentication result (i.e. SecurityContext) in the HttpSession.Subsequent requests from the same session will simply get the authentication result from the session rather than hitting the database to validate the credential .

Spring: BasicHttp doesn't work with jdbcAuthentication when hitting requests from Postman

I am trying to implement jdbcAuthentication in my project. The problem is that it works when I make requests from browser but when I make requests from Postman it doesn't work, I mean authentication doesn't happen and even random credentials work.
However if I use inMemoryAuthentication it works fine for both browser and Postman. This is my code inside SecurityConfig.
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource).usersByUsernameQuery("select username,password, enabled from users where username=?")
    .authoritiesByUsernameQuery("select username, role from user_roles where username=?");
// This works --> auth.inMemoryAuthentication().withUser("user").password("pass").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests().anyRequest().authenticated().and().httpBasic().and().csrf().disable();
}
In postman I make a POST request.
In Authorization type I select basic auth and enter the credentials which are then populated in the Authorization header below
These are the headers
Content-Type:application/json
X-XSRF-TOKEN:{{X-CSRF-TOKEN}}
Authorization:Basic ajhsjajywshhshshsssss
If I comment the jdbcAUthentication part and uncomment the inMomeryAuthentication code, authentication works from both broswer and postman. Can someone please help me, why is this happening? Am I missing something? Thanks !!
You are using .authenticated(), this means the user is already authenticated and you want to allow users that are already authenticated (aka "remembered").
http.authorizeRequests().anyRequest()
.authenticated().and().httpBasic().and().csrf().disable();
But actually, what you want to do is to authenticate for first time, so for this you need to change it to fullyAuthenticated
http.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and().httpBasic()
.and().csrf().disable();
If you check the AuthorizedUrl documentation you will find:
authenticated(): Specify that URLs are allowed by any authenticated user.
fullyAuthenticated():
Specify that URLs are allowed by users who have authenticated and were not "remembered".

Authentication is required to obtain an access token (anonymous not allowed)

I try to modify existing example - Tonr2 and Sparklr2.
Also I viewed this tutorial based on Spring Boot Spring Boot OAuth2. I try to build application like in Tonr2 example but without first login (on tonr2). I just need one Authentication on Sparklr2 side. I do this:
#Bean
public OAuth2ProtectedResourceDetails sparklr() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setId("sparklr/tonr");
details.setClientId("tonr");
details.setTokenName("oauth_token");
details.setClientSecret("secret");
details.setAccessTokenUri(accessTokenUri);
details.setUserAuthorizationUri(userAuthorizationUri);
details.setScope(Arrays.asList("openid"));
details.setGrantType("client_credentials");
details.setAuthenticationScheme(AuthenticationScheme.none);
details.setClientAuthenticationScheme(AuthenticationScheme.none);
return details;
}
But I have Authentication is required to obtain an access token (anonymous not allowed) . I checked this question. Of course, my user is anonymous - I want to login on Sparklr2. Also, I tried different combinations of settings of this bean, but nothing good. How to fix it? How to make it work as I want?
Almost two years late for the post.
The exception is thrown from AccessTokenProviderChain
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth instanceof AnonymousAuthenticationToken) {
if (!resource.isClientOnly()) {
throw new InsufficientAuthenticationException(
"Authentication is required to obtain an access token (anonymous not allowed)");
}
}
You either
Use ClientCredentialsResourceDetails in your OAuth2RestTemplate, or
Authenticate the user before using AuthorizationCodeResourceDetails to access external resources
In fact, in the tonr2 and sparklr2 example (I personally find the name very confusing), to access resources on sparklr2, a user has to first authenticate on tonr2. As seen in oauth2/tonr:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("marissa").password("wombat").roles("USER").and().withUser("sam")
.password("kangaroo").roles("USER");
}
If your user is anonymous, you might want to check for Single Sign On.
For whoever just want to quickly try out Oauth2 integration, add basic auth to your application:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated().and().httpBasic();
}
application.properties:
spring.security.user.password=password
spring.security.user.name=user
Don't forget to add spring-boot-starter-security to your project.
e.g. In gradle: compile 'org.springframework.boot:spring-boot-starter-security'
Or you can also disable AnonymousAuthenticationToken from creating by:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.anonymous().disable();
}
Old post...
The exception indeed is thrown form AccessTokenProviderChain but it happens when spring security filters invoking if incorrect order. Make sure that your OpenIdAuthenticationFilter is invoking after OAuth2ClientContextFilter.

Using Spring Security with zero-legged Oauth 1

I am writing an LTI application using Spring boot. LTI applications are basically a plug-in for a learning management system (in this case Canvas) which work by sending an Oauth1 signed POST to my server. The result of this request is displayed to the user inside of an iframe. There is a pre-shared key and secret that the LMS uses to sign the request. If the signature on the POST checks out, I have an authenticated user. I have this part working, partially based on this question.
During the initial request (which comes to the URL "/launch") I can call SecurityContextHolder.getContext().getAuthentication() and use this without problems. My problem is when the user makes another request, say for a picture or by clicking on a link in my content, the SecurityContext object isn't following them. I'm pretty sure I'm not setting up the Spring security filter chain correctly so the SecurityContextPersistenceFilter isn't being hit on subsequent requests. At the end of the day, SecurityContextHolder.getContext().getAuthentication() returns null.
The OAuth signature verification happens in a WebSecurityConfigurerAdapter like so: (again, based on this)
#Configuration
public static class OAuthSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
//spring auto-wiring to set up the
//zeroLeggedOauthProviderProcessingFilter (see linked question)
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/launch")
.addFilterBefore(zeroLeggedOAuthProviderProcessingFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests().anyRequest().hasRole("OAUTH")
.and().csrf().disable();
}
}
So this works and creates an authenticated principal and everything. But due to that antMatcher, it only applies to the /launch path.
It seems like it should be simple to add another security configurer adapter that will ensure that all other paths in the application are protected by an authenticated session and in so doing would cause the SecurityContext associated with this user to become available but I have been unable to come up with the magic sauce. The documentation focuses more on standard login form based authentication setups. I'm also kind of new to Spring in general so I'm clearly missing something. I tried this but it causes all other requests to return a 403:
#Configuration
public static class SessionSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().hasRole("OAUTH")
.and().csrf().disable();
}
}

Categories

Resources