Spring Security Not doing it's job - java

Atm I have a problem where the login page basically doesn't do anything because if you insert the url of a page, you can skip the login.
I'm using this
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").hasAnyRole("Administrator" , "Member")
//.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/dashboard")
.failureUrl("/login?error")
.successHandler(authenticationSuccessHandler)
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.logoutSuccessHandler(logoutSuccessHandler)
.and()
.csrf().disable();
}
Note that //.anyRequest().authenticated() is commented. This line seems to protect my website from accessing through the URL, it is redirecting to the login page.
But if I have it I can't see the css in my page and I get
Refused to apply style from 'http://localhost:8080/login' because its
MIME type ('text/html') is not a supported stylesheet MIME type, and
strict MIME checking is enabled.
How do I protect my website from knowing the URL but also see the CSS in the login page?

Decomment the anyRequest.authenticated
And then you must authorize the access to your static resources.
antMatcher("/css/**").permitAll()
Do the same for javascript and others static resources.
Full config :
http.authorizeRequests().antMatchers("/").hasAnyRole("Administrator" , "Member")
.anyRequest().authenticated()
.and()
.authorizeRequests()
.antMatchers("/css/**").permitAll()
.and()
.formLogin().permitAll()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/dashboard")
.failureUrl("/login?error")
.successHandler(authenticationSuccessHandler)
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.logoutSuccessHandler(logoutSuccessHandler)
.and()
.csrf().disable();

I usually configure the access for my resources overriding another configuration, in adition to the one you described:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
It seems more organized for me. You can add this in your Spring configuration class and change the 'resources' by you css. I usually pefer open the 'resources' and put my css inside (in a subdirectory) because it works for all kinds of resources, like images, css, and etc.

Related

What does .and() do in Spring Security classes like HttpSecurity?

In the below code example, what is the .and() actually doing?
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterAfter(new CsrfCookieGeneratorFilter(), CsrfFilter.class)
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.rememberMe()
.rememberMeServices(rememberMeServices)
.key(env.getProperty("jhipster.security.rememberme.key"))
.and()
.formLogin()
.loginProcessingUrl("/api/authentication")
.successHandler(ajaxAuthenticationSuccessHandler)
.failureHandler(ajaxAuthenticationFailureHandler)
.usernameParameter("j_username")
.passwordParameter("j_password")
.permitAll()
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler)
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.headers()
.frameOptions()
.disable()
.authorizeRequests()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/logs/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api/subscriptions").permitAll()
.antMatchers("/api/**").authenticated();
}
from the spring documentation :
The Java Configuration equivalent of closing an XML tag is expressed
using the and() method which allows us to continue configuring the
parent. If you read the code it also makes sense. I want to configure
authorized requests and configure form login and configure HTTP Basic
authentication.
So consider you have multiple configuration one for users which have admin role, and the second for the simple user role, to concatenate all them together we use and() method.
And this technique is a builder design pattern

How can I get session data in my controller after user is logged in

I have extended WebSecurityConfigurerAdapter in my Spring Boot MVC application. I also created the below method
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/")
.permitAll()
.antMatchers("/login")
.permitAll()
.antMatchers("/registration")
.permitAll()
.antMatchers("/dashboard")
.hasAuthority("ADMIN")
.anyRequest()
.authenticated()
.and()
.csrf()
.disable()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true")
.defaultSuccessUrl("/dashboard")
.usernameParameter("email")
.passwordParameter("password")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/home")
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied");
}
After the user logs in, I need some information like username, role, email, etc in my /dashboard controller. How can I achieve that?
I tried googling but could not find any concrete information about this.
Well, you have few options:
You can call the static method SecurityContextHolder.getContext().getAuthentication() (this way you can get the credentials all over you application)
If you are in the controller, you can have a Principle as a parameter to get the information
If you want the authentication token, you can have an Authentication as a parameter.
Note that you have to have some kind of mechanism to authenticate the user (openid with active-directory for instance) and make you server work with it.
See http://www.baeldung.com/get-user-in-spring-security for more information

Spring Security 404 page for unauthenticated users

I'm using Spring Boot and Thymeleaf. I have a custom 404 template page defined in src/main/resources/templates/error/404.html
This works properly when users are logged in.
However, when they are logged out, they do not get any type of 404 page, they just get redirected back to /login.
I'm thinking my security configuration needs to change but not sure what.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/","/register*","/resetPassword","/forgotPassword","/login","/404").permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN").anyRequest()
.authenticated().and().formLogin().loginPage("/login").failureUrl("/login?error")
.defaultSuccessUrl("/dashboard").successHandler(successHandler)
.usernameParameter("email").passwordParameter("password")
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login?logout").and()
.exceptionHandling().accessDeniedPage("/access-denied");
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/error**","/resources/**", "/static/**", "/css/**", "/js/**", "/img/**");
}
First of all I encourage you to use indentation when using java config to configure your security for your spring application. It helps with readability.
Note all top level methods on the first indentation (authRequest,formLogin,logout) all configure/update the HTTP object it self. All these elements are from the org.springframework.security.config.annotation.web.builders.HttpSecurity class.
The children of these classes further refine the HTTP security configuration.
http
.authorizeRequests()
.antMatchers("/","/register*","/resetPassword","/forgotPassword","/login","/404")
.permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN")
.anyRequest().authenticated() // <--------
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.defaultSuccessUrl("/dashboard")
.usernameParameter("email").passwordParameter("password")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied");
Note .anyRequest().authenticated() is specifically stating that any request must be authenticated. So when you attempt to goto any missing url on your domain it will ask you to login rather than goto the 404 page.
So if you remove that statement it and then try an goto a missing url page it will redirect you to a 404 page.
If you remove .anyRequest().Authenticated() then you can log in without authenticated.
Therefore, do not try to delete. For example, if you go to the address "http://localhost:8080/user", then you will be taken to the authorization page. And if you try to enter the page "http://localhost:8080/user/" then you will be taken to the user page. Please note that links differ only by the forward slash at the end. Of course if you remove ".anyRequest().Authenticated()" in this case you need to add more parameters to antMatchers like "/user" and "/user/"
Therefore, be careful and attentive.

Spring Security, secured and none secured access

I'm doing a little application that requires to login first. But for some 3rd party tool, I want to provide an API that doesn't require login. The login itself works fine, the API itself works, but I can't figure out how to tell Spring Security, that the API can be accessed without the need of authentication. I checked several topics here and on other websites and tried different versions, but none worked. Everytime I try to access the API, I get forwarded to the login form and have to login first.
My Code Looks like this so far, inside my Spring Security config:
/**
* configuration of spring security, defining access to the website
*
* #param http
* #throws Exception
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/rest/open**").permitAll()
.antMatchers("/login**").permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.defaultSuccessUrl("/dashboard")
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.logoutUrl("/j_spring_security_logout")
.logoutSuccessUrl("/login?logout")
.and()
.csrf();
}
And my controller:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class PredictionOpenRestController {
#RequestMapping("/rest/open/prediction")
public String getPrediction() {
return "First Try!";
}
}
Somehow I have to feeling to miss something.
See Spring Security Reference:
Our examples have only required users to be authenticated and have done so for every URL in our application. We can specify custom requirements for our URLs by adding multiple children to our http.authorizeRequests() method. For example:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated()
.and()
// ...
.formLogin();
}
1 There are multiple children to the http.authorizeRequests() method each matcher is considered in the order they were declared.
2
We specified multiple URL patterns that any user can access. Specifically, any user can access a request if the URL starts with "/resources/", equals "/signup", or equals "/about".
3
Any URL that starts with "/admin/" will be resticted to users who have the role "ROLE_ADMIN". You will notice that since we are invoking the hasRole method we do not need to specify the "ROLE_" prefix.
4
Any URL that starts with "/db/" requires the user to have both "ROLE_ADMIN" and "ROLE_DBA". You will notice that since we are using the hasRole expression we do not need to specify the "ROLE_" prefix.
5
Any URL that has not already been matched on only requires that the user be authenticated
Your second use of .authorizeRequests() overrides the first one.
Also see AntPathMatcher:
The mapping matches URLs using the following rules:
? matches one character
* matches zero or more characters
** matches zero or more directories in a path
Examples
com/t?st.jsp — matches com/test.jsp but also com/tast.jsp or com/txst.jsp
com/*.jsp — matches all .jsp files in the com directory
com/**/test.jsp — matches all test.jsp files underneath the com path
org/springframework/**/*.jsp — matches all .jsp files underneath the org/springframework path
org/**/servlet/bla.jsp — matches org/springframework/servlet/bla.jsp but also org/springframework/testing/servlet/bla.jsp and org/servlet/bla.jsp
Your modified code:
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/rest/open/**").permitAll()
.antMatchers("/login/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.defaultSuccessUrl("/dashboard")
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.logoutUrl("/j_spring_security_logout")
.logoutSuccessUrl("/login?logout")
.and()
.csrf();
}

In addition to token based authentication, allow Rest api endpoint with http basic auth

I have a recently created jhipster application with the following .yo-rc.json
{
"generator-jhipster": {
"baseName": "cmpayments",
"packageName": "au.com.cmx.myapp",
"packageFolder": "au/com/cmx/myapp",
"authenticationType": "token",
"hibernateCache": "no",
"clusteredHttpSession": "no",
"websocket": "no",
"databaseType": "sql",
"devDatabaseType": "postgresql",
"prodDatabaseType": "postgresql",
"useCompass": false,
"buildTool": "maven",
"frontendBuilder": "gulp",
"javaVersion": "8"
}
}
I like having the token based authentication on the webapp but I'd like the server to expose a REST api call with just http basic authentication. I've been battling with for a while but I'm completely new to Spring security and I'm hoping someone has already done this and can help me out.
I tried following the solution here:
Basic and form based authentication with Spring security Javaconfig
I created a second configuration with #Order(1) in SecurityConfiguration.java like so
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("api").password("pass").roles("API");
}
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/basicAuthApi/**").hasRole("API")
.and()
.httpBasic();
}
}
This works. If I hit an endpoint under /basicAuthApi with anything other than api/pass credentials, I get a 401. Yay.
However, after this, when I log in to the webapp as admin/admin (or user/user), I get logged in as anonymousUser. If I comment out the extra #Configuration in SecurityConfiguration.java and restart the app, that problem goes away and I get logged in correctly as admin (or user).
Interestingly, I tried changing the order of the second #Configuration to #Order(101) because I saw somewhere in one of the base classes an #Order(100). In this case the admin and user logins on the webapp work. But the rest api call is no longer secure i.e it succeeds even with incorrect password.
Does anyone know what I am doing wrong?
Thanks
dalyc
Replace the original SecurityConfiguration.configure :
http
.csrf()
.ignoringAntMatchers("/websocket/**")
.and()
.addFilterAfter(new CsrfCookieGeneratorFilter(), CsrfFilter.class)
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.rememberMe()
.rememberMeServices(rememberMeServices)
.rememberMeParameter("remember-me")
.key(env.getProperty("jhipster.security.rememberme.key"))
.and()
.formLogin()
.loginProcessingUrl("/api/authentication")
.successHandler(ajaxAuthenticationSuccessHandler)
.failureHandler(ajaxAuthenticationFailureHandler)
.usernameParameter("j_username")
.passwordParameter("j_password")
.permitAll()
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler)
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.headers()
.frameOptions()
.disable()
.and()
.authorizeRequests()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/account/reset_password/init").permitAll()
.antMatchers("/api/account/reset_password/finish").permitAll()
.antMatchers("/api/logs/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api/**").authenticated()
.antMatchers("/metrics/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/health/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/dump/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/shutdown/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/beans/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/configprops/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/info/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/autoconfig/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/env/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api-docs/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/protected/**").authenticated();
by this one :
http
.csrf()
.ignoringAntMatchers("/websocket/**")
.and()
.csrf()
.ignoringAntMatchers("/basicAuthApi/**")
.and()
.addFilterAfter(new CsrfCookieGeneratorFilter(), CsrfFilter.class)
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.rememberMe()
.rememberMeServices(rememberMeServices)
.rememberMeParameter("remember-me")
.key(env.getProperty("jhipster.security.rememberme.key"))
.and()
.formLogin()
.loginProcessingUrl("/api/authentication")
.successHandler(ajaxAuthenticationSuccessHandler)
.failureHandler(ajaxAuthenticationFailureHandler)
.usernameParameter("j_username")
.passwordParameter("j_password")
.permitAll()
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler)
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.headers()
.frameOptions()
.disable()
.and()
.authorizeRequests()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/account/reset_password/init").permitAll()
.antMatchers("/api/account/reset_password/finish").permitAll()
.antMatchers("/api/logs/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api/**").authenticated()
.antMatchers("/metrics/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/health/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/dump/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/shutdown/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/beans/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/configprops/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/info/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/autoconfig/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/env/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/api-docs/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/protected/**").authenticated()
.and()
.authorizeRequests()
.antMatchers("/basicAuthApi/**")
.hasAuthority(AuthoritiesConstants.USER).and().httpBasic();
I've only added :
.and()
.csrf()
.ignoringAntMatchers("/basicAuthApi/**")
and :
.and()
.authorizeRequests()
.antMatchers("/basicAuthApi/**")
.hasAuthority(AuthoritiesConstants.USER).and().httpBasic()
You can also create a new authority which can only access these web services.
An intro regarding the order first: The default order if you do not specify one, is the biggest possible number which maps to the lowest possible priority (since lower order translates to higher priority). Therefore when you added the configuration with order=1, you had two configurations, with the new one with order=1 having higher priority and being checked first.
In the scenario you describe where both configurations exist the following happens:
You try to log in to the webapp as admin/admin (or user/user), but spring security checks first the configuration with order=1 which has the ant matcher " .antMatchers("/basicAuthApi/**").hasRole("API")". It obviously does not match since the url you point is the one for the website, however the security does not fail because you are missing .anyRequest().authenticated() which would be needed in order to make the security check actually fail for users that failed to authenticate. Without this you actually pass the security check, although you are not authenticated i.e. you are considered an anonymous user with anonymous user access. Since spring security for that configuration succeeded, it does not even check the other one which is related to the website.
I found a solution that works for me. I realised that I don't need another #Configuration because the default jhipster configuration does not redirect to login page for unauthenticated access - it returns a 401 which is I want for the REST api too.
So I just registered a user with the username and password that I want to use for the REST api and added the following line to the bottom of the configure method in
OAuth2ServerConfiguration.ResourceServerConfiguration
`.antMatchers("/basicAuthApi/**").hasAuthority(AuthoritiesConstants.USER).and().httpBasic();`
I will now try to improve this by creating an API role and giving that role to users who need to call the REST API
I would still like to know why my original attempt had those problems if anyone knows.
Cheers
dalyc

Categories

Resources