In my application I have these roles:
Guest, User, Owner and Admin
I would like to use some kind of authorization where Admin can use all of the endpoints, while Owner can use all the functions that a User have. How should I achive this? What are good pratices?
If you have your securityConfig file setup and everything then all you need to do is permit access to different pages by different user roles, you do it as follow in your SecurityConfig class:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/homePage").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
.antMatchers("/userPage").access("hasRole('ROLE_USER')")
.antMatchers("/adminPage").access("hasRole('ROLE_ADMIN')")
.and()
.formLogin().loginPage("/loginPage")
.defaultSuccessUrl("/homePage")
.failureUrl("/loginPage?error")
.usernameParameter("username").passwordParameter("password")
.and()
.logout().logoutSuccessUrl("/loginPage?logout");
}
}
As you can see, home page is accessed by any user with ROLE_ADMIN or normal user (USER_ROLE) roles...
If you see the adminPage can only be accessed by users with ROLE_ADMIN roles...
You can use methods security. First of all, you need to enable method security, you can do this:
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true) //THIS IS THE KEY
public class SecurityConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
}
}
After enable, you can easily use security by method and user, like this:
#GetMapping("/ativas")
#PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_USER') and #oauth2.hasScope('read')")
public List<YourObject> findAll(){
return service.findAll();
}
This is a short answer.
First add Spring Security dependency to your pom.xml. Now use a class to configure Spring security by extending webSecurityConfigurerAdapter. Make sure you add #Configuration and #EnableWebSecurity annotations. Have a look at code below. This should help.
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser(id).password("{noop}" + pwd).roles("USER").and().withUser(admin_id).password("{noop}" + admin_pwd).roles("ADMIN", "USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
csrf().disable().authorizeRequests().antMatchers("/**").hasRole("ADMIN").
and().httpBasic();
}
Related
I wrote a simple backend software and I wanted to secure it with Spring Security and LDAP. It is obvious that LDAP part of the project works fine. the problem is that when I use the formLogin() for entring, that doesn't work and when I use postman it shows the result without asking user name and password! I think something in mywebSecurityConfig is wrong. this is my webSecurityConfig code:
#Configuration public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/**").permitAll()
.anyRequest().fullyAuthenticated();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=people")
.contextSource()
.url("ldap://localhost:10389/dc=example,dc=com")
.and()
.passwordCompare()
.passwordEncoder(new LdapShaPasswordEncoder() {
})
.passwordAttribute("userPassword");
} }
Use #EnableWebSecurity to enable Spring Security.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
and remove .antMatchers("/**").permitAll() from your configuration as it matches all the requests. This construct is normally used to specify specific whitelisted endpoints like static documentation that does not require security:
.antMatchers("/docs/**").permitAll()
I want the users to have access to html pages based on their role. For example only the ones with HR role should be able to view settings.html page and only those who are managers should see pendingrequest.html.
This is my security configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DatabaseAuthenticationProvider authenticationProvider;
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/js/**", "/css/**", "/img/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().loginPage("/login").failureUrl("/login").defaultSuccessUrl("/")
.and().logout().logoutSuccessUrl("/login")
.and().authorizeRequests().antMatchers("/login").permitAll()
.antMatchers("/settings.html").hasRole("HR")
.antMatchers("/pendingRequests.html").hasRole("MANAGER")
.antMatchers("/settings.html","/pendingRequests.html").hasRole("ADMIN")
.anyRequest().authenticated().and().csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider).eraseCredentials(false);
}
}
For some reason what I've tried there doesn't work, no matter what role I have I cannot see those pages.
Spring security by default adds ROLE_ prefix to all roles defined in security checks.
You can rename your role names to have ROLE_ prefix or remove that prefix from your configuration ( How do I remove the ROLE_ prefix from Spring Security with JavaConfig? )
The task is to create a Web Application that should be accessible from different users with different roles. Some roles see all pages, some see only a few.
I have the following config for the SecurityConfig, but it's not working.
#Configuration
#EnableWebSecurity
#Import(value = { SecurityWebApplicationInitializer.class })
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
private AuthenticationService authenticationService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
Md5PasswordEncoder encoder = new Md5PasswordEncoder();
auth.userDetailsService(authenticationService).passwordEncoder(encoder);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login", "/page1").permitAll()
.antMatchers("/**").access("hasRole('RADMIN')")
.antMatchers("/login", "/page2").access("hasRole('ADMIN')")
.antMatchers("/page2").hasAnyRole("RADMIN", "ADMIN")
.and().formLogin()
.and().exceptionHandling().accessDeniedPage("/403");
}
}
I've tried changing hasAnyRole() to hasAnyAuthority(), no effect. Pretty much every change I make either doesn't let ADMIN to login (he only sees /403 or /404) or lets anyone, authorised or not, to see everything.
Well... the problem was as stupid as it can get.
Seems like with one of the commits the database name was changed, thus there was no role (ROLE_NONE) to authenticate...
I am using Spring-Security 4.0.1.RELEASE and there are two important tables which are users and authorities. Table authorities should have exactly two columns (username, authority) and the role should be with prefix ROLE_ e.g. ROLE_ADMIN & ROLE_RADMIN like Andreas pointed out.
Try to keep your authentication simple like below and see does it make any difference then add the additional pages and the roles, e.g.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().
anyRequest().authenticated().and().formLogin().loginPage("/login.html")
.loginProcessingUrl("/login").permitAll().and().logout().logoutSuccessUrl("/")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).permitAll().and().exceptionHandling()
.accessDeniedPage("/403.html");
}
I am using spring-security 3.2.0.RC2 with java config and two HttpSecurity configurations. One for REST API and one for UI.
When I post to /logout it redirects to /login?logout but then (incorrectly) redirects to /login.
When i enter username and password successfully I get redirected to login?logout and have to enter credentials a second time to get to the main page.
So it seems like the permitAll for login is not being honored for login?logout.
My security config looks like this:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Resource
private MyUserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
StandardPasswordEncoder encoder = new StandardPasswordEncoder();
auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
}
#Configuration
#Order(1)
public static class RestSecurityConfig
extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/v1/**").authorizeRequests()
.antMatchers("/v1/admin/**").hasRole("admin")
.antMatchers("/v1/account/**").hasRole("admin")
.antMatchers("/v1/plant/**").access("hasRole('admin') or hasRole('dataProvider')")
.antMatchers("/v1/upload/**").access("hasRole('admin') or hasRole('dataProvider')")
.antMatchers("/v1/**").authenticated()
.and().httpBasic();
}
}
#Configuration
#Order(2)
public static class UiSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/account/**").hasRole("admin")
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/plant/**").access("hasRole('admin') or hasRole('dataProvider')")
.antMatchers("/upload/**").access("hasRole('admin') or hasRole('dataProvider')")
.anyRequest().authenticated()
.and().formLogin().loginPage("/login").permitAll();
}
}
}
Can anyone explain why this is happening or what is wrong with my configuration?
A secondary problem that I see with this configuration is that the jsp tag sec:authorize url=... does not work although sec:authorize access=... does work.
In the url=... case it always shows the content even if the user is not authorized.
I know the user is not authorized becuase hitting the link that should have been hidden by the sec:authorize tag results in a 403 Forbidden.
Any help on this greatly appreciated!
I found a workaround for this apparent bug.
I added permitAll() on /login/** as follows:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/account/request/**").permitAll()
.antMatchers("/login/**").permitAll()
.antMatchers("/account/change_password/**").authenticated()
.antMatchers("/account/**").hasAuthority("admin")
.antMatchers("/admin/**").hasAuthority("admin")
.antMatchers("/plant/**").hasAnyAuthority("admin", "dataProvider")
.antMatchers("/upload/**").hasAnyAuthority("admin", "dataProvider")
.anyRequest().authenticated()
.and().formLogin().loginPage("/login").permitAll();
}
Answering my own question in case it helps anyone else who runs into this bug.
Instead of this:
.antMatchers("/login/**").permitAll()
I think the better solution would be this:
http
.authorizeRequests()
.antMatchers("/account/request/**").permitAll()
.*antMatchers("/login").permitAll()
.antMatchers("/account/change_password/**").authenticated()
.antMatchers("/account/**").hasAuthority("admin")
.antMatchers("/admin/**").hasAuthority("admin")
.antMatchers("/plant/**").hasAnyAuthority("admin", "dataProvider")
.antMatchers("/upload/**").hasAnyAuthority("admin", "dataProvider")
.anyRequest().authenticated()
.and().formLogin().loginPage("/login").permitAll().
.and().logout().logoutSuccessUrl("/login?logout").permitAll();
The reason being, url pattern for permitAll(), in this case has limited scope when compared to "/login/**"
I am having some issues getting my application set up using method level annotation controlled by #EnableGlobalMethodSecurity I am using Servlet 3.0 style initialization using
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(MultiSecurityConfig.class);
}
}
I have attempted 2 different ways of initialising an AuthenticationManager both with their own issues. Please note that not using #EnableGlobalMethodSecurity results in a successful server start up and all of the form security executes as expected. My issues arise when I add #EnableGlobalMethodSecurity and #PreAuthorize("hasRole('ROLE_USER')") annotations on my controller.
I am attempting to set up form-based and api-based security independently. The method based annotations need only work for the api security.
One configuration was the following.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class MultiSecurityConfig {
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**").httpBasic();
}
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
}
#Configuration
public static class FormWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/static/**","/status");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().hasRole("USER").and()
.formLogin().loginPage("/login").permitAll();
}
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
}
}
This is not ideal as I really want only a single registration of the authentication mechanism but the main issue is that it results in the following exception:
java.lang.IllegalArgumentException: Expecting to only find a single bean for type interface org.springframework.security.authentication.AuthenticationManager, but found []
As far as I am aware #EnableGlobalMethodSecurity sets up its own AuthenticationManager so I'm not sure what the problem is here.
The second configuration is as follows.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class MultiSecurityConfig {
#Bean
protected AuthenticationManager authenticationManager() throws Exception {
return new AuthenticationManagerBuilder(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR)
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN").and()
.and()
.build();
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**").httpBasic();
}
}
#Configuration
public static class FormWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/static/**","/status");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().hasRole("USER").and()
.formLogin().loginPage("/login").permitAll();
}
}
}
This config actually starts successfully but with an exception
java.lang.IllegalArgumentException: A parent AuthenticationManager or a list of AuthenticationProviders is required
at org.springframework.security.authentication.ProviderManager.checkState(ProviderManager.java:117)
at org.springframework.security.authentication.ProviderManager.<init>(ProviderManager.java:106)
at org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder.performBuild(AuthenticationManagerBuilder.java:221)
and when I test I found that the security doesn't work.
I've been looking at this for a couple of days now and even after diving into spring security implementation code I can't seem to find what is wrong with my configuration.
I am using spring-security-3.2.0.RC1 and spring-framework-3.2.3.RELEASE.
When you use the protected registerAuthentication methods on WebSecurityConfigurerAdapter it is scoping the Authentication to that WebSecurityConfigurerAdapter so EnableGlobalMethodSecurity cannot find it. If you think about this...it makes sense since the method is protected.
The error you are seeing is actually a debug statement (note the level is DEBUG). The reason is that Spring Security will try a few different ways to automatically wire the Global Method Security. Specifically EnableGlobalMethodSecurity will try the following ways to try and get the AuthenticationManager:
If you extend GlobalMethodSecurityConfiguration and override the registerAuthentication it will use the AuthenticationManagerBuilder that was passed in. This allows for isolating the AuthenticationManager in the same way you can do so with WebSecurityConfigurerAdapter
Try to build from the global shared instance of AuthenticationManagerBuilder, if it fails it logs the error message you are seeing (Note the logs also state "This is ok for now, we will try using an AuthenticationManager directly")
Try to use an AuthenticationManager that is exposed as a bean.
For your code, you are going to be best off using something like the following:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class MultiSecurityConfig {
// Since MultiSecurityConfig does not extend GlobalMethodSecurityConfiguration and
// define an AuthenticationManager, it will try using the globally defined
// AuthenticationManagerBuilder to create one
// The #Enable*Security annotations create a global AuthenticationManagerBuilder
// that can optionally be used for creating an AuthenticationManager that is shared
// The key to using it is to use the #Autowired annotation
#Autowired
public void registerSharedAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
// Since we didn't specify an AuthenticationManager for this class,
// the global instance is used
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.httpBasic();
}
}
#Configuration
public static class FormWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
// Since we didn't specify an AuthenticationManager for this class,
// the global instance is used
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/static/**","/status");
}
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().hasRole("USER")
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
}
}
NOTE: More documentation around this will be getting added to the reference in the coming days.