Authorize requests dynamically in spring-security - spring-security

Many of the examples I have seen authorize requests by hard-coding or XML config:
protected void configure(HttpSecurity http) {
http
.authorizeRequests(authorize -> authorize
.mvcMatchers("/resources/**", "/signup", "/about").permitAll()
.mvcMatchers("/admin/**").hasRole("ADMIN")
.mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().denyAll()
);
}
But for many systems, shouldn't this information be stored in a database-like storage?
protected void configure(HttpSecurity http) {
http.authorizeRequests(authorize -> authorize
.rulesFromDB(...) // This is the most common situation, right?
);
}
Of course, spring-security does not have such a method.
Does spring-security provide other mechanisms for dynamic authorization?
In order to become familiar with spring's dynamic authorization mechanism, is it a good start point to learn SecurityMetadataSource and its implementations in spring-security?

Related

User Impersonation with Spring Security

I am trying to implement a user impersonation using Spring Security and its SwitchUserFilter.
Currently the Configuration looks as follows:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/impersonate*").hasRole("Administrator");
http.addFilter(switchUserFilter());
super.configure(http);
setLoginView(http, ViewLogin.class);
}
...
#Bean
public SwitchUserFilter switchUserFilter(){
SwitchUserFilter filter = new SwitchUserFilter();
filter.setUserDetailsService(userDetailsService);
filter.setSwitchUserUrl("/impersonate");
filter.setSwitchFailureUrl("/switchUser");
filter.setTargetUrl("/");
return filter;
}
And I was trying to navigate to the impersonation using:
UI.getCurrent().getPage().setLocation("/impersonate?username="+username);
Unfortunately Vaadin is trying to navigate to the page "/impersonate..." but doesn't find a corresponding Page and skips the SwitchUserFilter.
How would I force the switch?
If you use setSwitchUserUrl it will match only POST requests [1].
But you want to use a GET request. So you have to use a matcher like
this:
filter.setSwitchUserMatcher(new AntPathRequestMatcher("/impersonate", "GET"));
BTW: you don't have to addFilter the filter, if you define it as
a #Bean.
[1]
https://github.com/spring-projects/spring-security/blob/e125a76687d4ca9739cd663eedc107c7ff55e8cf/web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java#L513-L515

How to make certain endpoint in spring security to be allowed without authentication?

I'm developing an application using Spring security.
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.csrf().disable().authorizeRequests().antMatchers("/api/client/findByVariable?variable=").permitAll();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().requestMatchers()
.antMatchers("/api/**").and().authorizeRequests().antMatchers("/api/**")
.access("#oauth2.hasScope('read') or (!#oauth2.isOAuth() and hasRole('USER'))");
// #formatter:on
}
How can I modify above code so that endpoint "/api/client/findByVariable?variable=" can be allowed to be accessed without requiring authentication just as if there was no Spring Security ?
I tried adding the line :
http.csrf().disable().authorizeRequests().antMatchers("/api/client/findByVariable?variable=").permitAll();
But it is not working
You can ignore endpoints with overriding the configure method that gives you a WebSecurity instance:
#Override
public void configure(WebSecurity web)
{
web.ignoring().antMatchers("/api/client/findByVariable");
}
I'm not sure if you can also match by query params like /api/client/findByVariable?variable=*

How to config different authentication filter by url pattern?

I have a spring boot application that provides mainly REST endpoints, auth with JWT. I want use JWT with secret1 to authenticate /internal_api/** API, and JWT with secret2 for others. I don't knows how to configuration for this scenes. Does need two SecurityConfig Class to configure?
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfig : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.antMatchers("/internal_api/**")
.addFilterAt(JwtTokenAuthFilter("secret1"), UsernamePasswordAuthenticationFilter::class.java)
.authorizeRequests()
.anyRequest().permitAll()
}
}
I expect user with JWT(secret1) can access /internal_api/** (through JwtTokenAuthFilter("secret1"))
and use with JWT(secret2) can access /other_resource/** (through JwtTokenAuthFilter("secret2")
You can just use the same JwtTokenAuthFilter. The point is that you have to decide which roles or authority should an user has in order to access these endpoints. Then during authenticating JWT in JwtTokenAuthFilter , you determine if the current requested user have these roles. If yes , make sure the user object that set into SecurityContext are assigned with these roles.
Suppose /internal_api/** requires ROLE_INTERNAL to access while /other_resource/** requires ROLE_OTHER to access . The configuration looks like:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/internal_api/**").hasRole("INTERNAL")
.antMatchers("/other_resource/**").hasRole("OTHER")
//blablbabla.....
}
Through these days online search, I finally found a better implementation.
According to spring official document recommended method.
Creating and Customizing Filter Chains Section:
Many applications have completely different access rules for one set of resources compared to another. For example an application that hosts a UI and a backing API might support cookie-based authentication with a redirect to a login page for the UI parts, and token-based authentication with a 401 response to unauthenticated requests for the API parts. Each set of resources has its own WebSecurityConfigurerAdapter with a unique order and a its own request matcher. If the matching rules overlap the earliest ordered filter chain will win.
class SecurityConfig {
#Configuration
#Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
class InternalApiConfig: WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.antMatcher("/internal_api/**")
http.authorizeRequests()
.antMatchers("/internal_api/**").authenticated()
http.addFilterAt(JwtTokenAuthFilter("secret1"), UsernamePasswordAuthenticationFilter::class.java)
}
}
#Configuration
#Order(SecurityProperties.BASIC_AUTH_ORDER - 9)
class ApiConfig : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.authorizeRequests()
.antMatchers("/other_resource/**").authenticated()
http.addFilterAt(JwtTokenAuthFilter("secret2"), UsernamePasswordAuthenticationFilter::class.java)
}
}
}

Prevent session creation when using basic auth in spring security

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.

Spring Security OAuth2 - optional login with server check

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.

Resources