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

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

Related

Do you know what problem I have with my spring security config?

I need to authenticate all requests starting from "/api/auth" except "/api/auth/login" and "/api/auth/token/refresh". This is my security config configure method. Problem is, it does not work as intended. It checks for authentication for "/api/auth/token/refresh" even though I assigned permitAll to it.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/auth/login").antMatcher("/api/auth/token/refresh")
.antMatcher("/api/auth/**")
.antMatcher("/api/**");
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(AUTH_WHITELIST)
.permitAll()
.antMatchers("/api/auth/login","/api/auth/token/refresh").permitAll()
.antMatchers("/api/auth/**").authenticated()
.antMatchers("/api/**").permitAll()
.anyRequest()
.permitAll();
// Add our custom JWT security filter
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
Remove the first 3 lines of code.
Javadoc of antMatcher(String) says:
Invoking antMatcher(String) will override previous invocations of [...], antMatcher(String), [...].
Calling it 4 times doesn't do what you think it does.

Spring Boot WebSecurityConfig LogoutSuccessURL with invalidSessionUrl

I am working on a spring boot project and WebSecurityConfig for session control. The issue is that when the session expires, i am being redirected to /?sessionexpired when my expectation is that it should redirect o /?expiredsession. Also when user logout the page is redirected to /?sessionexpired and not /?logout.
My config is as follow
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/thirdparty/**", "/webjars/**", "/sessionerror").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/")
.failureUrl("/?error")
.defaultSuccessUrl("/dashboard")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/?logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")) // override default of only allowing POST for logout so we can use a GET
.deleteCookies("remove")
.invalidateHttpSession(true)
.permitAll()
.and()
.sessionManagement()
.maximumSessions(1)
.expiredUrl("/?expiredsession")
.maxSessionsPreventsLogin(false)
.and()
.invalidSessionUrl("/?sessionexpired");
}
Can someone please help me figure this out.
So i eventually figured it out. It is redirecting to /?sessionexpired because on logout i am invalidatingSession by setting it to true. I should set it to false and then use .deleteCookies("JSESSIONID") to invalidate the session. This way it will redirect properly.

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

How to keep indentation hierarchies in fluent method chains on automatic formatting

Say you have fluent method chains with meaningful indentations like:
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
Auto formatting in Eclipse creates the following, if you have "Never join already wrapped lines" active:
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
Because of that you are forced to decide between autoformatting a file containing fluent code and losing the meaningful indention hierarchy or giving up on automatic formatting...
I'd need the option "don't change relative indentation for chained method calls".
Is there a solution for this?

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