Helllo, I'm using RESTful with basic authentication and this code is a part from the RestController:
#GetMapping("/jpa/users/{username}/goals")
public List<Goal> getAllGoals(#PathVariable String username) {
userId = getUserIdFromUsername(username);
return goalJpaRepository.findByUserId(userId);
}
public Long getUserIdFromUsername(String username) {
User user = userJpaRepository.findByUsername(username);
userId = user.getId();
return userId;
}
And I have a problem, for example I'm using Postman to retrieve the goals for a speciffic user like this:
http://localhost:8080/jpa/users/john/goals with GET request
Then I use the basic authentication for the username john and the password for this username and I receive the goals for john.
After that if I do a GET request for this link http://localhost:8080/jpa/users/tom/goals I receive the goals for tom, but I'm logged in with john at this moment of time, so john can see his goals and also he can see tom's goals.
The question is how can I access the login username in the RestController, because I want to do something like this:
if (loginUsername == username) {
return goalJpaRepository.findByUserId(userId);
}
return "Access denied!";
So I want to know if it is possible to access the login username from HTTP Header?
Thank you!
UPDATE - Yes the framework is Spring Boot, also I'm using Spring Security with Dao Authentication because I want to get the user from a MySQL database. Anyway I'm not an expert at Spring Security.
Now I understand how to use Principal in my controller methods, but I don't know how to use Spring Security for this specific case. How should I implement it? For example the user john should see and modify only his goals.
Spring Security Configuration:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.dgs.restful.webservices.goaltrackerservice.user.MyUserDetailsService;
#Configuration
#EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter {
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
private MyUserDetailsService userDetailsService;
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider
= new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(bCryptPasswordEncoder());
return authProvider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/allusers").permitAll()
.anyRequest().authenticated()
.and()
// .formLogin().and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
}
Assuming you are using Spring as your Java framework, you should use Spring security to configure the basic authentication. Many tutorials available online (https://www.baeldung.com/spring-security-basic-authentication,
Spring Security will then provide a security context available throughout the app (SecurityContextHolder.getContext()) from which you can retrieve the connected user information (username, ...).
For instance to retrieve the username of the connected user, you should do :
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String loginUsername = authentication.getName();
Alternatively, as mentioned by #gervais.b Spring can inject the Principal (or Authentication) in your controller methods.
As said by #Glains An even better alternative is to use the #PreAuthorize and #PostAuthorize annotations, which alows you to define simple rules based on Spring Expression Language.
Please note that you are not doing any security at this time.
As said by #Matt "It depends which framework you are using". But I guess you are using spring. You should then have a look at the spring-securuty module documentation.
Basically you can inject the authenticated user into your method parameter :
#GetMapping("/jpa/users/{username}/goals")
public List<Goal> getAllGoals(#PathVariable String username, Principal principal) {
if ( username.equals(principal.getName()) ) {
userId = getUserIdFromUsername(username);
return goalJpaRepository.findByUserId(userId);
} else {
throw new SomeExceptionThatWillBeMapped();
}
}
But spring-security and many frameworks provide better patterns to manage the security.
You can also solve this problem with #PreAuthorize, an annotation offered by the Spring Security Framework, which uses the Spring Expression Language.
#PreAuthorize("principal.name == #username")
#GetMapping("/jpa/users/{username}/goals")
public List<Goal> getAllGoals(#PathVariable String username) {
return goalJpaRepository.findByUserId(userId);
}
Behind the scenes Spring will use the already mentioned SecurityContextHolder to fetch the currently authenticated principal. If the expression resolves to false, the response code 403 will be returned.
Please note that you have to enable global method security:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
}
To answer your new question on "Dao Authentication" the answer is to provide a custom UserDetailsService.
From the configuration that you have attached to your question it looks that you already have a MyUserDetailsService.
There are plenty of articles that explain how to use a custom DetailsService. This one seems to match your requirements : https://www.baeldung.com/spring-security-authentication-with-a-database
Edit: On how to be sure that only John can see John's items.
Basically, the only ting that you can do to esnure that only John can see his goals it to restrict the goals to only those owned by John. But there is plenty way of doing this.
As you suggest in your initial question, you can just select the goals for a specific user. The power with spring-security is that it can inject a Principal but also kind of orher authentication object.
You can also make that more implicit an filter a the DAO/Repository side by using the SecurityContextHolder. This approach is fine and looks better when your system is more user centric or like a multi-tenant system.
Using some specific #Annotations or Aspects would also be a solution but maybe less obvious in this case.
Related
I would like to access the http request, specifically auth header in AuthenticationManager.authenticate() context.
Requirement is to authenticate a custom token. There is an external library which does that and so I don't have the luxury to read out principal from the token. Hence, in the custom filter, I am returning the full token in the getPreAuthenticatedPrincipal() method. This seems borderline incorrect and I would like to not pass the token pretending it to be principal.
Is there any way I can get it without violating any framework constraints?
Or is there a better way to handle the scenario which I'm trying to achieve?
Here's the config class:
#Configuration
#EnableWebSecurity(debug = true)
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity httpSecurity) throws Exception{
CustomTokenFilter customTokenFilter = new CustomTokenFilter();
customTokenFilter.setAuthenticationManager(new CustomAuthenticationMgr());
httpSecurity
// csrf etc etc
.addFilter(customTokenFilter)
.authorizeRequests()
.mvcMatchers("/users/**")
.authenticated()
.and()
.authorizeRequests()
.mvcMatchers("/other-api/**")
.permitAll()
.and()
.httpBasic();
}
Here's the custom token filter class:
public class CustomTokenFilter extends AbstractPreAuthenticatedProcessingFilter {
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
String authorization = request.getHeader("authorization");
if(authorization.indexOf("Custom") == 0){
return Map.of("Custom",authorization.split(" ")[1]);
}
return null;
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "";
}
}
And finally, the custom authentication manager class:
public class CustomAuthenticationMgr implements AuthenticationManager {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Map<String,String> map = (Map) authentication.getPrincipal();
String token = map.get("Custom");
// Custom validation - checking length here just to simplify
if(token.length() > 0)
authentication.setAuthenticated(true);
return authentication;
}
}
Version: Spring Boot 2.6.7 (transitive: spring-core 5.3.19)
Constraints: Cannot upgrade to other versions at the moment
Thanks in advance!
You're right, this isn't a good way to do it. (It's great you noticed -- too few people care whether their code is idiomatic.)
A better way would be to start by writing your own filter that actually just... does the authentication. You can extend OncePerRequestFilter rather than something more specific. That's what Spring Security itself does, both for basic authentication (BasicAuthenticationFilter) and for OAuth bearer tokens (BearerTokenAuthenticationFilter). You may want to take a careful look at the code for BearerTokenAuthenticationFilter since the problem it solves is very similar to yours. (I wouldn't extend it, though, since it's very clearly intended to do OAuth specifically. I wouldn't straight up copy the code either -- it's fairly simple as Spring Security filters go but probably still does more than you need. Try to understand the code instead; that will help a lot with your understanding of Spring Security in general.)
Okay, so you have a filter which looks a lot like BearerTokenAuthenticationFilter. That is, it contains an AuthenticationManager and its doFilter method consists of extracting the token from the request, passing that into the AuthenticationManager and then doing some SecurityContext-related stuff. Except, problem: AuthenticationManager.authenticate() expects an Authentication, not a String, and the token is a String.
The solution is to write a wrapper object for your token which implements Authentication. You can do this a couple of ways. Personally, what I'd do is use two classes: one which you pass into AuthenticationManager.authenticate(), and one which you get back. So we have, say, CustomTokenAuthenticationRequest implements Authentication and CustomTokenAuthentication implements Authentication. Both are immutable.
CustomTokenAuthenticationRequest basically just contains the token; its isAuthenticated() is return false, its getPrincipal() returns the token and its getCredentials() also returns the token. This is essentially what Spring Security itself does with BearerTokenAuthenticationToken.
CustomTokenAuthentication, on the other hand, probably contains a UserDetails of some sort; its isAuthenticated() is return true, its getName() is a username or user id or something, etc.
Now you need to teach the AuthenticationManager to authenticate CustomTokenAuthenticationRequests. The way to do this isn't to implement AuthenticationManager, it's to implement AuthenticationProvider. So you write a class that looks roughly like
public class CustomTokenAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication a) {
String token = ((CustomTokenAuthenticationRequest) a).getToken();
if (/* the token is valid */) {
CustomTokenAuthentication returnValue = // whatever you want it to be
return returnValue;
}
throw new BadCredentialsException("Invalid token");
}
#Override
public boolean supports(Class<?> authClass) {
return authClass == CustomTokenAuthenticationRequest.class;
}
}
Finally, wire it all up. Add the authentication provider to your HttpSecurity using its authenticationProvider() method. (If you do this, and you don't change the default authentication manager configuration, authenticationProvider() results in your authentication provider getting added to an AuthenticationManager which Spring Security configures for you -- an instance of ProviderManager.) Add the filter using addFilterAt(BasicAuthenticationFilter.class). Also, don't call httpBasic() because this adds a BasicAuthenticationFilter which I am guessing you don't want. Or maybe you want basic authentication and also your custom token authentication? But you didn't say that. If you do want both, you'll want to add your filter with addFilterBefore or addFilterAfter, and you need to think about ordering. Generally filter ordering is important in Spring Security.
I glossed over a lot of stuff here, barely gave you any code, and still wrote something of blog post length. Spring Security is very complex, and the thing you're trying to do isn't easily done in an idiomatic manner if you don't have much experience. I highly recommend just reading the Spring Security reference documentation from start to finish before you try implementing any of my suggestions. You'll also need to read quite a lot of Javadoc and tutorials and/or framework code. If there's something specific you want to follow up on I might respond to a comment, but I don't promise it; I had to do some research for this answer and have already spent more time on it than I planned to.
you should look spring-security-lambda-dsl,add filter,add auth provider
Is it possible to use OAuth2 for certain endpoints in my rest application and use basic authentication too for some other endpoints.
It should all work on spring security version 2.0.1.RELEASE. I hope someone can help me further.
Yes, it's possible to use a basic authentication as well as an OAuth2 authentication intertwined, but I doubt you'll be able to set it up easily as HttpSecurity's authenticated() method doesn't allow you to pick which of your authentication method (oauth2Login/formLogin) will work.
However, there's a way to easily bypass that:
You could add a custom authority, let's call it ROLE_BASICAUTH, when an user connects using basic auth, and ROLE_OAUTH2 when an user connects using OAuth2. That way, you can use
.antMatchers("/endpoint-that-requires-basic-auth").hasRole("BASICAUTH")
.antMatchers("/endpoint-that-requires-oauth2").hasRole("OAUTH2")
.anyRequest().authenticated()
When they reach an endpoint that you want basic authentication (and not OAuth2), you check their current authorities, and if it's not BASICAUTH, then you invalidate their session you display a login form without OAuth2 (to force them to use the basic authentication).
The downside to doing that is that you'd need to implement both a custom UserDetailsService as well as a custom OAuth2UserService...
But that's actually not that hard:
#Service
public class UserService extends DefaultOAuth2UserService implements UserDetailsService {
// ...
#Override
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
OAuth2User user = super.loadUser(oAuth2UserRequest);
Map<String, Object> attributes = user.getAttributes();
Set<GrantedAuthority> authoritySet = new HashSet<>(user.getAuthorities());
String userNameAttributeName = oAuth2UserRequest.getClientRegistration().getProviderDetails()
.getUserInfoEndpoint().getUserNameAttributeName();
authoritySet.add(new SimpleGrantedAuthority("ROLE_OAUTH2"));
return new DefaultOAuth2User(authoritySet, attributes, userNameAttributeName);
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = getUserFromDatabase(username); // you'll need to provide that method (where are the username/password stored?)
if (user == null) { // UserDetailsService doesn't allow loadUserByUsername to return null, so throw exception
throw new UsernameNotFoundException("Couldn't find user with username '"+username+"'");
}
// add ROLE_BASICAUTH (you might need a custom UserDetails implementation here, because by defaut, UserDetails.getAuthorities() is immutable (I think, I might be a liar)
return user;
}
}
Note that this is a rough implementation, so you'll have to work it out a bit on your end as well.
You can also use this repository I made https://github.com/TwinProduction/spring-security-oauth2-client-example/tree/master/custom-userservice-sample as a guideline for the custom OAuth2UserService
Good luck.
I'm trying to implement a simple spring boot project. I got several REST-Endpoints which I've to secure differently. One has to be secured by Basic Auth, another one with OAuth and one with a custom security implementation.
REST-Endpoints:
/basic/auth
/application/secure (oauth)
/application/secure2 (own implementation)
From tutorials, I know I've to set the order of the security adapters. My first intention was to set the order in steps of ten (e.g. #Order(10), #Order(20)) in case I need to add other security filters in between. By doing so I investigated the following behavior:
If I add the basic auth filter with #Order(10) and an OAuth filter with #Order(20) only the OAuth filter works.
If I add the basic auth filter with #Order(1) or #Order(2) and an OAuth filter with #Order(4) both filters works.
If I add a filter to #Order(3) I receive an error which says, that order 3 is already in use and cannot be configured twice.
So there is a default spring security adapter (or whatever) which has the default order 3. I thought I disable every default spring security behavior by adding #EnableWebSecurity. After I did not find an answer by google my questions would be:
Am I doing the right things?
What is this security adapter with order 3 by spring?
Does the default security adapter block my basic auth implementation?
WebSecurityConfig:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Order(10)
#Configuration
public class BasicAuthConfig extends WebSecurityConfigurerAdapter {
#Value("${security.user.password}")
private String password;
#Value("${security.user.name}")
private String username;
private static final String ROLE_ADMIN = "ADMIN";
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser(username).password(password).roles(ROLE_ADMIN);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestMatchers().antMatchers("/basic/**", "/") //
.and().authorizeRequests().anyRequest().authenticated() //
.and().httpBasic();
}
}
#Order(20)
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class Oauth2ServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
System.out.println("Filter called");
// #formatter:off
http.csrf().disable();
http.authorizeRequests().antMatchers("/application/**").authenticated()
// .antMatchers(GET, "/application/secure").authenticated()
.anyRequest().authenticated();
// #formatter:on
}
// offline token validator
}
This is an old question, but if anyone is still wondering what the issue is, here are my observations:
#EnableResourceServer imports ResourceServerConfiguration, which has an order of 3.
There are ways that may allow you to add more than 2 filters before the order 3 resource server configurer, for instance
by giving some of them negative order values (Although I don't suppose negative values would be any special, one would need to take into account other implicit web security configurers -- for instance the one with order 0 -- enabled by default. This however means there is a possibility of collision between filters in different versions of the framework as new features are introduced);
by adding them as resource configurers (The ResourceServerConfiguration class does not add any request matchers, but enforces a fallback to anyRequest().authenticated() if the user has not configured anything).
For a better understanding on how paths are matched in the configured request matchers, you can take a quick glance at Ant path patterns.
If I have a superclass User, and classes Student and Lecturer extending this class, what is the best way to use roles (i.e. for security) in the springBoot application.
I feel like it is almost not necessary because I have two classes anyway?
Using the class types as roles is not a very good idea as it is not flexible. Lets say you create a user that is a Student and persist it into database table called student. If you later want to change the role of this user to a Lecturer, you will have to migrate all the users data to a new table.
A common approach that I often see being used is having a intermediate class (table) which holds all the available roles. Then each user has a list of roles (or only one role). This way you can easily change what roles apply to different users.
For example lets say you have a basic role class:
class Role {
String name;
// Getters and setters...
}
Then your student and lecturer classes would look something like this:
abstract class User {
List<Role> roles;
// Easy way to check if a user has a role (better approach would be to use a Set).
boolean hasRole(String name) {
for (Role r : roles) {
if (name.equals(r.getName()))
return true
}
return false
}
}
class Lecturer extends User {
}
class Student extends User {
}
This type of role structure has one obvious benefit - it will be very easy to change roles for the users on your database.
Also you can create multiple tiny roles. For example a Lecturer could have the roles read, write, create and so on, which would grant him all the rights to your application. While the student might only have the read role.
I would also suggest to checkout Spring Security if you plan to implement some sort of authentication to you application (it also follows a similar method of roles that I've described).
I would add security roles anyway.
You do not have to use this classes when handling every request. While authenticated user will have his roles "hide" in session tokens.
I am not sure if you can handle security problems only with two user classes.
And what about not authenticated users?
if you want to make security in your spring boot app you could use spring securtiy which handles roles and make it easy for you , you could also create your own succes handlers and failure handlers classes :
first you add these lines on your pom.xml under dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
you have to be able to manipulate thymleaf for login custom forms wich is not complicated ,after defining thymleaf and spring security you need to define a configuration class and data source like i did in the code below
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import javax.sql.DataSource ;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void GlobalConfig(AuthenticationManagerBuilder auth,DataSource dataSource) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
//this is actually where your roles and your user class interferes to login with different roles take this as exemple to your own need
.usersByUsernameQuery("SELECT login AS principal , password AS credentials , true FROM utilisateur WHERE login = ? ")
.authoritiesByUsernameQuery("SELECT u.login AS principal , r.role as role FROM utilisateur u ,role r where u.id_role=r.id_role AND login = ? ");
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/angular/**","/css/**","/js/**","/assets/**");
//this line to make full access of assets
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/yourHomePages/ThatDoesn'tIncludeLogin") .permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.invalidateHttpSession(true)
.logoutUrl("/logout")
.permitAll();
}
}
after defining the configuration class and data sources you need to define \login so you have to resolve the path by making MvcResolver like this
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
public class WebMVCconfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
viewName is you custom login that you'll create and it has to be under src/main/resources/templates/
for any further informations you could use these links
http://www.thymeleaf.org/doc/articles/springsecurity.html ,https://github.com/thymeleaf/thymeleafexamples-springsecurity and also spring security official documentation
I am studying Spring Security and I have the following doubts related the difference between the use of the #Secured annotation and the #RolesAllowed annotation.
I know that both have to been used at method level, on my study material I found the followings 2 examples:
#RolesAllowed annotation:
import javax.annotation.security.RolesAllowed;
public class ItemManager {
#RolesAllowed("ROLE_MEMBER")
public Item findItem(long itemNumber) {
...
}
}
#Secured annotation:
import org.springframework.security.annotation.Secured;
public class ItemManager {
#Secured("ROLE_MEMBER")
public Item findItem(long itemNumber) {
...
}
}
It seems to me that these 2 annotations works in the same way. What are the differences? What am I missing?
Another doubt that I have is: what exactly represent the ROLE_MEMBER?
I think that this is something like role based security, so it could mean something like: only if the user is a member it could access to the annoted resource (is it correct?). But where and how is definied the fact that the user have setted this role (it is a member)? How exactly works?
Tnx
#Secured and #RolesAllowed are the same. They do the same operation in Spring.
But
#RolesAllowed - Standard annotation of Java.
Java has defined Java Specification Request, basically change requests for the Java language, libraries and other components. For the development of annotations, they have provided JSR 250. #RolesAllowed is included in it. This link contains further info in JSR 250
#Secured - Spring security annotation
ROLE_MEMBER is the role which is set to the security user details.
Refer this example from my current project. Here I'm using the user data object and mapping the roles given to the user to the security user details.
public class CustomUserDetails implements UserDetails {
...
...
...
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
for (Role role : this.user.getRoles()){
grantedAuthorities.add(new SimpleGrantedAuthority(role.getRole()));
}
return grantedAuthorities;
}
}
These roles are then set for the security approvals using the #Secured or #RolesAllowed or #PreAuthorize("hasRole('ROLE_USER')") for the methods.
By design it's good to put the security in the Service layer. So when I'm securing my service actions, I check for the roles, not for the users.
This way, we can focus on the business logic and the security for the business logic via small security units called roles.
Then I assign the roles to the user. Users can have multiple roles. So you have to see the relationship here. Users are given the roles. And roles are given the access to the business logic. Users are given the access to the business logic via the roles. This concept is called, Role Based Access Control.
And in complex situations we can also manage hierarchical roles. Where one role has many other roles. But in the UserDetails, we have to flatten the role hierarchy and provide the list of roles to the Spring framework to process.
The accepted answer completely answers the question (heh), but I think this is a good place to say how to enable method level security in Spring.
The only thing You need to add is the #EnableGlobalMethodSecurity annotation on a configuration class (see the example) with the following properties set to true (default is false)
securedEnabled (enables Spring's Secured annotation.),
jsr250Enabled (enables the JSR-250 standard java security annotations, like RolesAllowed),
prePostEnabled (enables Spring's PreAuthorize and PostAuthorize annotations).
Example of annotation usage:
#EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.formLogin(); // You probably need more than this
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
// your authentication manager config here
}
For more detailed example, see Spring Security Method Level Annotations Example.