I am new to spring security and was checking how to authorize requests to URLs in my application.
According to the documentation here, we add authorization as follow:
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();
}
As this method worked fine for me, I was wondering if there's another dynamic way to specify this configuration. By using some sort of annotations for our REST controllers for example?
I have a solution in mind that would be really practical, but I wanted to make sure that there's no other way to do this before starting to develop my own code.
Thank you for your help.
Yes there is an annotations as #Secured/#PreAuthorize/#PostAuthorize. this annotations are preferred way for applying method-level security, and supports Spring Expression Language out of the box, and provide expression-based access control.
for e.g
#PreAuthorize("hasRole('ADMIN')")
public String yourControllerMethod() {
return response;
}
for detail check here.
The only other way is to use the #Secured/#PreAuthorize/#PostAuthorize annotations. But you must put them on all webservices you want to secure.
Usually, when I build a webservices application, I like to authorize all requests on the WebSecurityConfigurerAdapter, and then secure requests one by one with these annotations.
Related
I have a Spring Boot based application. I want the URL /camunda/app/welcome/default/#!/login to be accessible without any authentication, while the URLs
/camunda/app/welcome/default/#!/welcome,
/camunda/app/welcome/default/#!/dashboard,
/camunda/app/tasklist/**, and
/camunda/app/admin/**
must be secured (i. e. only authenticated users should be able to access them).
To achieve this, I wrote the following configuration:
#Configuration
#EnableWebSecurity
public class MyConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.and()
.authorizeRequests()
.antMatchers("/camunda/app/welcome/default/#!/login").permitAll()
.antMatchers("/camunda/app/welcome/default/#!/welcome",
"/camunda/app/welcome/default/#!/dashboard",
"/camunda/app/tasklist/**",
"/camunda/app/admin/**",
"/oauth2/authorization/**",
"/oauth2/code/myredirecturl")
.authenticated()
.and()
.oauth2Login(...)
.logout()
.logoutRequestMatcher(...)
.logoutSuccessHandler(...);
}
}
However with this configuration unauthenticated users can access URLs that are supposed to be protected (/camunda/app/welcome/default/#!/welcome, /camunda/app/welcome/default/#!/dashboard, /camunda/app/tasklist/**, /camunda/app/admin/**).
What is wrong with my configuration and how can I fix it?
Sadly to say, but that will not work, because there is actually only one url:
/camunda/app/welcome/default/
and parts after '#' symbol are called 'anchors':
#!/welcome,
#!/dashboard,
Anchors are not processed on backend, because they point to some place in html document that was loaded on client side.
https://www.w3docs.com/snippets/html/how-to-create-an-anchor-link-to-jump-to-a-specific-part-of-a-page.html
So you cant solve it by Spring only, there must be some frontend logic.
Also these two masks:
/camunda/app/tasklist/, and
/camunda/app/admin/
could be covered by Spring Boot, because point to different urls, not anchors.
Make sure you use the URL encoding of #, which is %23 when calling the endpoints. Otherwise, the characters after the # will not be considered.
Making a request to /camunda/app/welcome/default/#!/welcome without properly encoding will be interpreted as a request to /camunda/app/welcome/default/. Since that endpoint doesn't require authentication then anyone will be allowed to access it.
Since all endpoints except /camunda/app/welcome/default/#!/login require authentication you condense your HttpSecurity configuration. I'll rewrite it below using the lambda style configuration to make it more readable:
http
// no need to add requestMatchers since you aren't changing the default configuration
.authorizeRequests(authz -> authz
.antMatchers("/camunda/app/welcome/default/#!/login").permitAll()
.anyRequest().authenticated() // any request that does not match the above rule ^ will require an authenticated user
)
.oauth2Login(...)
.logout(...)
I have a spring restful application, backend - Spring 2.4.3, frontend - Angular, when I trying to restrict access to individual pages, I get 401 code. I've tried all variations of hasRole () and hasAuthority () nothing helps. What am I doing wrong?
SecurityConfig.java
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/login", "/registration").permitAll()
.antMatchers("/profile","/profile/*").hasAnyAuthority("USER","ADMIN","INTERVIEWER")
.antMatchers("/getAllUsers").permitAll()
.anyRequest().authenticated();
http
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
/*.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class)*/
.cors();
}
Role.java
#XmlType
#XmlEnum
public enum Role implements GrantedAuthority {
ADMIN,
USER,
INTERVIEWER;
#Override
public String getAuthority() {
return this.name();
}
}
Result:
something wrong :(
Based on your provided code, the line .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class) is commented out. I can't speak to what happens when you un-comment that line (since it is a custom filter), but without that line, you have no means of authenticating. This results in your entry point (which is not provided in your example) being invoked, and seems to be returning your 401 status code.
You can test this by commenting out the lines:
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
and adding .formLogin().and() instead. Form Login will provide a default authentication entry point, default authentication filter, and (if using spring boot) default user details service with a randomly generated password printed to your console, which you can use to test logging in. See the docs for more info on this.
A note on testing with hello world (out of the box) configuration: It is a very useful technique to use formLogin() for testing authorization rules (e.g. .antMatchers("/profile","/profile/*").hasAnyAuthority("USER","ADMIN","INTERVIEWER")) in Spring Security. It allows you to eliminate your authentication mechanism from being the problem. Once you are confident your authorization rules are working, you can move on to configuring your own authentication scheme. When possible, seek to utilize an existing scheme provided by Spring Security, and only create a custom filter when you cannot use an out of the box scheme. You can read about JWT authentication in the docs.
I've set up some security filters in my Spring Boot application and I have defined specific URL patterns I want
#Bean
public FilterRegistrationBean<CustomAuthenticationFilter> authenticationFilter(){
FilterRegistrationBean<CustomAuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new CustomAuthenticationFilter());
registrationBean.addUrlPatterns("/data/*", "/record/*","/records/*","/storage/*","/query/*");
return registrationBean;
}
I'm adding the filter into the WebConfig like so
#Override
public final void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(new CustomAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.antMatcher(ApplicationInfoService.API_PATH + "/info")
.authorizeRequests().anyRequest().permitAll();
}
When I make a request to something like the following;
http://localhost:8080/api/record/v1/record/search?offset=0&count=0
the filter isn't activated. However, I did notice the url in the antMatcher did activiate the filter, so maybe there's something there.
Either way, I'm still trying to grasp how much of the api path the addUrlPattern needs? Where or what else do I need to add beside adding addFilter<Before|After> in the WebSecurityConfig class.
I have taken a look at a whole lot of examples and questions in SO, but none seem to have helped me. Hoping someone can help me understand what else I could be missing.
Javadoc of .antMatcher(...) says
Allows configuring the HttpSecurity to only be invoked when matching the provided ant pattern.
This means that your security is only ever applied to /info requests. All others bypass security.
The code you wrote is the same as:
http.antMatcher(ApplicationInfoService.API_PATH + "/info");
http.addFilterAfter(new CustomAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
http.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
http.authorizeRequests()
.anyRequest().permitAll();
Note that the following are two entirely different things:
http.antMatcher("/foo/**");
http.authorizeRequests()
.antMatchers("/foo").permitAll()
.anyRequest().hasRole("ADMIN");
Without the first, security processes all requests. With it, only requests with path /foo are processed by the security module, while all other requests entirely bypasses security.
The second specifies that requests with path /foo are permitted, and that all other requests requires a user with role ADMIN.
I want to restrict certain rest endpoints to be only for LDAP users in a certain group.
I followed the guide https://spring.io/guides/gs/authenticating-ldap/ to setup LDAP authentication which is working perfectly. So how do I restrict certain rest endpoints?
I tried
#PreAuthorize("hasRole('developers')")
#RequestMapping("/foo")
public String foo(HttpServletRequest request) {
return "Welcome to FOO " + request.getRemoteUser();
}
but it still lets users not in the developers group access that endpoint
You can modify your WebSecurityConfigurerAdapter configuration to something like:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.antMatchers("/foo").hasRole("developers")
.and()
.formLogin();
}
I am not exactly sure of the syntax and if that first rule will override your second rule, but it will be similar to that.
Or, you can try configuring security on a method by method basis like this sample.
#EnableGlobalMethodSecurity(securedEnabled=true) needed to be added to the webSecurityConfig. Once I did that I was able to use #Secured("ROLE_DEVELOPERS") and that method was then restricted to that role.
I am using Spring-security in a Spring-boot appplication. By default, all methods are restricted to authenticated users thanks to this configuration :
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
Now, I would like to mark some urls as public. I could do it with ant matchers but I would prefer to be able to directly mark the relevant methods with an annotation.
I saw that some #PreAuthorize annotation exist but I could have it work only if I remove those lines from the configuration :
.authorizeRequests()
.anyRequest().authenticated()
This forces me to annotate manually every method that I want to secure with this annotation :
#PreAuthorize("isAuthenticated()")
This is highly dangerous because every forgotten url will be publicly accessible by default. Is there a way to make every url accessible to authenticated users by default and open some urls with
#PreAuthorize("permitAll()")
Also, I saw in another post that the OP was answered :
But it is really a bad idea to use pre-post annotation on a controller, for what could be done directly in HttpSecurity configuration. It forced you to use proxyTargetClass = true.
What is wrong with that? (Also, I did not need to use proxyTargetClass = true)