How can I prevent/avoid the session creation when I request to my RESTful api using basic auth?
My application has a front-end that needs sessions, but some of my others applications communicate with it via the RESTful api. In that cases I need to prevent the session creation because my app has a session management that limit 1 session per user.
My problem today is that this others apps invalidate the active browser session because the other app are using the same user who the browser has used.
After some research I found a solution using Multiple HttpSecurity configurations. I created 2 security configurations.
My primary now is only applied if a specific HEADER is present and at this configuration the session management is STATELESS:
#Override
protected void configure(HttpSecurity http) throws Exception {
//#formatter:off
http.requestMatcher(new RequestMatcher() {
#Override
public boolean matches(HttpServletRequest request) {
return request.getHeader(CheckVersionAspect.ESB_VERSION_HEADER) != null ||
request.getHeader(CheckVersionAspect.PDV_VERSION_HEADER) != null;
}
}).authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//#formatter:on
}
And the second configuration continues with the session management to limit 1 session per user.
Credits to this answer.
Related
I have an application where users/applications can authenticate either with an OpenID provider or with a JWT token.
Here is my spring security configuration class.
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService()).and()
.and()
.oauth2ResourceServer()
.jwt();
}
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
return oidcUserRequest -> {
OidcUserService oidcUserService = new OidcUserService();
OidcUser oidcUser = oidcUserService.loadUser(oidcUserRequest);
return oidcUser;
};
}
}
It's working as expected but I would like to disable session creation for the JWT authorization part. Do I need to split this into multiple configurations? I understand that if we have multiple configuration classes we need to differentiate based on URL pattern which I can't do in my case as a user authenticated via OpenId or via JWT still should be able to access the same URLs.
Here is the complete sample code in Github.
I solved by splitting the configuration into two classes. One for OAuth login and the other for the resource server. Configured
http.requestMatcher(new RequestHeaderRequestMatcher("Authorization"))
on the resource server Configuration class and made it's Order as 1 and Open Id configuration order as 2. In Resource server configuration I have disabled session creation.
In this way, if any external clients are calling with a JWT token with header 'Authorization' then it will be handled by Resource server configuration or else it will be handled by the second/OAuth configuration.
I have a Spring Boot REST application which has two main parts:
UI where I want to protect the ajax calls with a token
public endpoints where I want to have Basic Auth
As far as I understand I can't protect the public endpoints with CSRF tokens, as these need a session. The problem is, some endpoints need to be reachable by both, so how can I protect them with CSRF when it is used by the UI and disable CSRF for Basic Auth?
Here is what I currently have, where I disable csrf completely so basic works...
http.requestMatchers().antMatchers("/form/fill", "/form/fill/*", "/form/fillParams", "/form/fillParams/*").and()
.csrf().disable().authorizeRequests().anyRequest().hasAnyRole(SecurityConfiguration.ROLE_FORMS_AUTHOR,
SecurityConfiguration.ROLE_FORM_FILLER, SecurityConfiguration.ROLE_ADMIN)
.and().httpBasic();
EDIT: I found this old answer and I wonder if there is a way I can leverage this for my case, but I'm still not sure how to distinguish between a "local" user and one that is authenticated with httpBasic()
In your Spring Security java configuration file you can configure the HttpSecurity object as follows in order to enable the CSRF check only on some requests (by default is enabled on all the incoming requests, and disable will disable for all incoming request so request Mather can help here for path you want to enable or disable csrf.).
Make sure to replace /urls-with-csrf-check/** with your paths by end point or multiple paths..
#Override
protected void configure(HttpSecurity http) throws Exception {
RequestMatcher csrfRequestMatcher = new RequestMatcher() {
private RegexRequestMatcher requestMatcher =
new RegexRequestMatcher("/urls-with-csrf-check/**", null);
public boolean matches(HttpServletRequest httpServletRequest) {
if (requestMatcher.matches(httpServletRequest)) {
return true;
}
return false;
}
};
http.requestMatchers().antMatchers("/form/fill", "/form/fill/*", "/form/fillParams", "/form/fillParams/*").and()
.csrf()
.requireCsrfProtectionMatcher(csrfRequestMatcher)
.and()
.authorizeRequests().anyRequest().hasAnyRole(SecurityConfiguration.ROLE_FORMS_AUTHOR, SecurityConfiguration.ROLE_FORM_FILLER, SecurityConfiguration.ROLE_ADMIN)
.and().httpBasic();
}
With the input from #kj007, I was able to get this working.
I am using the requireCsrfProtectionMatcher and this is how my matcher looks like:
public class UIRequestMatcher implements RequestMatcher {
public static final List<GrantedAuthority> USER_ROLES = new ArrayList<>();
static {
USER_ROLES.add(new SimpleGrantedAuthority(SecurityConfiguration.ROLE_ADMIN));
USER_ROLES.add(new SimpleGrantedAuthority(SecurityConfiguration.ROLE_FILES_AUTHOR));
USER_ROLES.add(new SimpleGrantedAuthority(SecurityConfiguration.ROLE_FORMS_AUTHOR));
USER_ROLES.add(new SimpleGrantedAuthority(SecurityConfiguration.ROLE_TEMPLATES_AUTHOR));
}
#Override
public boolean matches(HttpServletRequest request) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return "POST".equals(request.getMethod()) && auth.getAuthorities().stream().anyMatch(USER_ROLES::contains);
}
}
So I am checking if the Authentication has any of my user roles, as my basic auth should only be used for my technical users.
I'm working on a Web Project with different Spring Boot WebMVC Clients. Some of this Clients needs a authorization and I solved it with a Spring Security OAuth2 Server. The authentication works fine and I had no problems. Some Clients didn't need an login and they are public for all.
Technical facts: All clients use a mix between Angular, jQuery and simple JSP's. All apps use Spring Security and the public app configuration is like this:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/fonts/**").permitAll()
.anyRequest().authenticated();
}
Now my question: I plan to build a Login (or Logout) Button in the Header of all apps. In the apps with a required authentication is that no problem. I can check the principal is null or not. But how can I solve this in public apps. The principal is ever null and the client didn't check the authentication status with the server. I had some ideas to fix it but nothing is working. The best way would be a automatic check in Spring Boot. But how can I configure this? Maybe I can check it with JavaScript, but my shots also didn't work.
Maybe it would help - two of my apps:
https://www.planyourtrip.travel (public application)
https://profile.planyourtrip.travel (memberonly application)
UPDATE: Maybe a better example
If I configure a public app like this
#Configuration
#EnableOAuth2Sso
public static class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/**").permitAll()
.anyRequest().authenticated();
}
}
and the MVC Controller like this
#RequestMapping("/{([a-z]{2})}")
public ModelAndView start(final Principal principal) {
return new ModelAndView("start");
}
then is the Principal ever null. I think that is my Problem. I need a check with the OAuth Server and if i logged in is the principal set and if I'm not logged in it should be null.
If I had understood your question correctly, than you need that some URL pattern can be accessed without authentication. Than in that case you can use the following method to prevent authentication for certain URL patterns -
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/urlPattern");
}
permitAll() method defines that all the authenticated users can access mentioned URL pattern. So if you want some users to access some resources (URL) without authentication, than you have to use above method.
Spring Security 4 enables developers to write less code, but sometimes this also causes thing easily to get out of control. For example, now I am writing a login function, once the user pressed a button (login/unionauth URI), an OAuth 2.0 like authentication offered by a 3rd party would be launched, and finally the result comes back and we compare the user with our local database. In order to do so, first I have adapter class like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**", "/webjars/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))
.and()
.logout()
.logoutSuccessUrl("/").permitAll()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
Then I have ssoFilter() like this:
public class UnionAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
// authentication steps.
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("tom", "N/A", null);
AuthenticationManager manager = this.getAuthenticationManager();
return manager.authenticate(token);
}
Now, the problem is, manager is null. Why it is null? I think in case of password username mode, the auth.userDetailsService(myUserDetailsService); in adapter would enable a DAO manager. In official website, it has this:
How to Add a Local User Database
Many applications need to hold data about their users locally, even if
authentication is delegated to an external provider. We don’t show the
code here, but it is easy to do in two steps.
Choose a backend for your database, and set up some repositories (e.g.
using Spring Data) for a custom User object that suits your needs and
can be populated, fully or partially, from the external
authentication.
Provision a User object for each unique user that logs in by
inspecting the repository in your /user endpoint. If there is already
a user with the identity of the current Principal, it can be updated,
otherwise created.
Hint: add a field in the User object to link to a unique identifier in
the external provider (not the user’s name, but something that is
unique to the account in the external provider).
Any idea how to add database into Spring Security OAuth2 or why the manager in the first paragraph is null?
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