I have a spring-boot application with spring-security enabled. I have a custom AbstractPreAuthenticatedProcessingFilter that checks request header for some authentication token set by external system. In case token is absent or invalid corresponding AuthenticationManager throws AuthenticationException. Looks pretty simple.
My problem is that I'd like to be able to handle this AuthenticationException by replying with a 403 (Forbidden) status. How can I do it? I tried adding the following to WebSecurityConfigurerAdapter:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint());
...
but it doesn't work. The issue is that ExceptionTranslationFilter that is supposed (if I understand it correctly) to handle these things is the last filter in the default security filter chain and exceptions thrown just never get to it. I'm totally stuck. How is this supposed to work? Please help!
A long time ago you question was done, but I was stucked in the same question without solution, so I decided to explain how to resolve this.
You are allowed to edit the FilterChain, you may put one ExceptionTranslationFilter before your PreAuthenticationFilter as ...
http
.csrf().disable()
.addFilter(myPreAuthenticationFilter)
.addFilterBefore(new ExceptionTranslationFilter(
new Http403ForbiddenEntryPoint()),
myPreAuthenticationFilter.getClass()
)
.authorizeRequests()
.antMatchers(authSecuredUrls).authenticated()
.anyRequest().permitAll()
.and()
.httpBasic()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint())
;
Your AbstractPreAuthenticatedProcessingFilter getPreAuthenticatedCredentials or getPreAuthenticatedPrincipal methods may throw AuthenticationException if any credential is not provided as ...
if ( !StringUtils.hasText(request.getParameter("MY_TOKEN")) ) {
throw new BadCredentialsException("No pre-authenticated principal found in request");
}
Or your AuthenticationManager authenticate method can throw AuthenticationException too.
Related
I override configure(HttpSecurity http) method in SampleSecurityConfig Class like this
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/delete/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin().and().httpBasic();
}
If i don't use httpBasic method, it seems no problem occurred.
what does httpBasic method exactly do?
Calling this method on HttpSecurity will enable Http Basic Authentication for your application with some "reasonable" defaults.
It will return a HttpBasicConfigurer for further customization.
You can test this by curl and passing a header like Authorization: Basic bzFbdGfmZrptWY30YQ== but base64 encoding a valid username/password combination.
Documentation for httpBasic
What are we saying by calling httpBasic() ?
When httpBasic() is called, we are telling Spring to authenticate the request using the values passed by the Authorization request header. If the request is not authenticated you will get a returned status of 401 and a error message of Unauthorized
What is actually happening when httpBasic() is called ?
By calling httpBasic(), an instance of the BasicAuthenticationFilter is added to the filter chain. The BasicAuthenticationFilter will then proceed with trying to authenticate the request in the typical Spring Security fashion. If authentication is successful, the resulting Authentication object will be placed into the SecurityContextHolder, which can then be used for future authentication purposes.
I've spent the last 3-4 days working with Spring Security (4.0.2) for the first time and have been able to get it to work thanks to pouring over the Spring Security samples, numerous posts at SO and other blogs.
However, as I've been trying out different options I've was stumped for several hours with adding sessionManagement to HttpSecurity. It appears that the order of the options matters and I'm really curious why that is and why it doesn't seem to be mentioned anywhere in the Spring Security documentation, or anywhere else that I could find for that matter?
For example, if you place sessionManagement first then the next configuration (authorizeRequests in this case, but it doesn't matter which one is next) gets a syntax error noted in the code sample below
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.invalidSessionUrl("/login?invalid=1")
.maximumSessions(1)
.expiredUrl("/login?time=1")
.maxSessionsPreventsLogin(true);
.authorizeRequests() //<<< The method authorizeRequests() is undefined for the type SecurityConfig
.antMatchers("/", "/home", "/login**", "/thankyou").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?err=1")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/thankyou")
.deleteCookies( "JSESSIONID" )
.invalidateHttpSession(false);
}
It appears sessionManagement must be the last config. Why?
Also, once I put sessionManagement last it made a different where the invalidSessionUrl method is placed. I originally had it last as shown below with the syntax error:
.sessionManagement()
.maximumSessions(1)
.expiredUrl("/login?time=1")
.maxSessionsPreventsLogin(true)
.invalidSessionUrl("/login?invalid=1");
//<<< The method invalidSessionUrl(String) is undefined for the type SessionManagementConfigurer<HttpSecurity>.ConcurrencyControlConfigurer
After a couple of hours I figured out that invalidSessionUrl and maximumSessions are methods of SessionManagementConfigurer and expiredUrl and maxSessionsPreventsLogin belong to SessionManagementConfigurer.ConcurrencyControlConfigurer and the only way the code will compile is if the ConcurrencyControlConfigurer methods are placed after the SessionManagementConfigurer methods.
Again, I'm would really like to know why so I can be alert to this sort of thing as I learn other Spring interfaces. In other words, I'd really like to know if there is an architectural design or programming convention involved here that I'm not yet aware of as a newbie to Spring.
BTW, the webinar by Rob Winch was super helpful! Here's the link if anyone is interested: Webinar Replay: Spring Security 3.2
Add a couple of and() and it will compile:
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.invalidSessionUrl("/login?invalid=1")
.maximumSessions(1)
.expiredUrl("/login?time=1")
.maxSessionsPreventsLogin(true)
.and()
.and()
.authorizeRequests()
...
One indentation level in indicates a new return type (only for clarity). The and() returns to the previous type.
I am trying to configure spring boot-Embedded Tomcat basic HTTP authentication with multiple roles, with most of the url's similar but few of them specific to each role. Here for first role the basic HTTP authentication pops up and working fine. With below code,
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class TestSecurityAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers(null, getAppAdminRolePaths()).authenticated()
.anyRequest().hasAnyRole("APPADMIN")
.and()
.httpBasic();
http.csrf().disable()
.authorizeRequests().antMatchers(null, getAppUserRolePaths()).authenticated()
.anyRequest().hasAnyRole("APPUSER")
.and()
.httpBasic();
http.authorizeRequests().antMatchers(null, new String[]{"/app/appOwnerView.html"}).authenticated()
.anyRequest().hasAnyRole("APPOWNER")
.and()
.httpBasic();
}
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("appadminname").password("appadminpwd").roles("APPADMIN").and()
.withUser("appusername").password("appuserpwd").roles("APPUSER").and()
.withUser("appownername").password("appoownerpwd").roles("APPOWNER");
}
private static String[] getAppAdminRolePaths(){
return new String[]{"/appweb/*",
"/app/checkService.html",
"/app/index.html",
"/app/testData.html",
"/app/adminView.html",
"/app/demo.html"};
}
private static String[] getAppUserRolePaths(){
return new String[]{"/appweb/*",
"/app/checkService.html",
"/app/index.html",
"/app/testData.html",
"/app/userView.html",
"/app/demo.html"};
}
}
For HTTP username/password popup in browser with url http://localhost:8080/app/index.html say with appadminname/appadminpwd it works fine. But for same url if I enter appusername/appuserpwd it throws HTTP 403 Forbidden access error. Here why is the second role APPUSER configured is throwing this error is I am not sure. Please let know if some way to get this resolved.
Thanks
I appreciate this question is a little old now, but this may still be useful to someone.
Firstly, I'm not sure why your calls to antMatchers() supply null as the first argument; antMatchers() expects a list of strings defining the URLs to be covered by this rule, so I'm not sure what null is expected to match in this case.
Secondly, anyRequest() means that this rule will be applied to any request made to the application regardless of the URL used, and Spring will apply security rules in the order that they are defined. You would typically define URLs and their associated roles first, and then default to a rule for any other request that must be authenticated (but does not necessarily need any specific roles) with something like anyRequest().authenticated()
Your first rule says that any request made to the application must be made by users with the role APPADMIN, which denies you access when you try to log in as appusername, so the second rule to allow APPUSERs is not even processed.
Thirdly, you are making multiple calls to http.authorizeRequests() when you should probably actually be chaining them together, for example:
http.csrf().disable().authorizeRequests()
.antMatchers( getAppAdminRolePaths() ).hasRole("APPADMIN")
.antMatchers( getAppUserRolePaths() ).hasRole("APPUSER")
.anyRequest().authenticated();
Lastly, when you have just a single role to check against, you can use hasRole() instead of hasAnyRole().
You also don't need to supply authenticated() and hasRole() in the same rule because hasRole() implies that the user is already authenticated.
You can find more explanations and examples in the Spring documentation: http://docs.spring.io/spring-security/site/docs/4.0.3.RELEASE/reference/htmlsingle/#authorize-requests
I've got spring security configured as
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = false)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.jee()
.mappableRoles("ROLE1", "ROLE2");
}
}
And then #Secured annotations with roles on the rest endpoints.
Doesn't matter what I do I don't seem to be able to create a custom handler for authorization (i.e. a user logged in successfully but doesn't have the right role to access a particular endpoint) error events.
What I tried was:
An exception handler with #ExceptionHandler(value = AccessDeniedException.class) - doesn't get called. I understand that's by design, ok.
AuthenticationEntryPoint configured as
http.exceptionHandling().authenticationEntryPoint(new RestAuthenticationEntryPoint())
#Component( "restAuthenticationEntryPoint" )
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence( HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException ) throws IOException {
// logging
}
}
-doesn't get called
ApplicationListener - I can see it's getting called on context closed, so it's registered correctly but not called on authorization error.
All I need is a simple handler to log unsuccessful authorization events.
It completely slipped my mind that the allowed roles are listed in web.xml as well for j2ee container authentication to work. So any user without a least one of those roles was just being rejected by the container.
Otherwise the first, simplest, method works fine. Hopefully my mistake will help someone in the future
Using Spring Security
Basic authentication setup works against some hardcoded username passwords as shown here:
http://thoughtfulsoftware.wordpress.com/2013/12/08/adding-security-to-spring-guides-rest-service/
So trying to extend it to use LDAP.
Completed the set up for LDAP authentication with our LDAP server
Now when I try to call my rest service through REST Console plug-in an authorization window keeps on popping up for username password. If I cancel it authorization fails, not sure where I am going wrong
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.csrf().disable()
.httpBasic();
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder
.ldapAuthentication()
.userSearchFilter("(uid={0})").userSearchBase("ou=people,dc=zzz,dc=xxxx,dc=yyy")
.groupRoleAttribute("cn").groupSearchFilter("(member={0})")
//.userDnPatterns("uid={0},ou=people")
//.groupSearchBase("ou=groups")
.contextSource().url("ldaps://ldap.xxxx.yyy:636/cn=cw-grpreader,ou=people,dc=xxx,dc=xxxx,dc=xxx")
.managerDn("cn=xx-xxr,ou=people,dc=med,dc=xxxx,dc=xxx")
.managerPassword("#$%^^");
}
This is one way I tried which give the recurring authentication popup
if I cancel the popup I get
HTTP Status 401 - [LDAP: error code 49 - NDS error: failed authentication (-669)]; error even if the credentials are correct
Link to some tutorial will be really helpful
This is due to httpbasic authentication that you have set up. Spring boot generates a random password thats shown on console which you could use.
To disable that pop-up simply add this to your application.properties file.
security.basic.enabled: false