I am new to Spring Security. I have a Spring Boot application with two different types of entities. Customers and employees. In Employees I have normal employees, admins and super users. Since I am using JPA, each entity has its own repository. How to model my UserDetailsService with loadUserByUsername since this is a common method to validate against many repositories. Is there anything that I am missing to model around my entities?
Additional Info:
In my design, I have two entities. Customer and Employee. Employee will have roles like NORMAL, ADMIN and SUPER_USER. Customer is a different entity.
Will there be two UserDetailsService and two AuthenticationProvider each pointing to its own table (Customer and Employee)?
As your requirement is to have multiple authentication entry points it is not as simple as Atul's answer.
What you need is
You need to differentiate customer and employee while logging in. (Preferred way radio button)
You need to implement your custom authentication filter i.e, implementation of UsernamePasswordAuthenticationFilter instead of spring-security provided default .formLogin()
Create two UsernamePasswordAuthenticationToken as EmployeeUsernamePasswordAuthenticationToken and CustomerUsernamePasswordAuthenticationToken
In your custom filter get userType from request and based on userType set authToken as empAuthToken or customerAuthToken to differentiate required authentication provider.
Create AuthenticationProvider as EmployeeCustomAuthenticationProvider and CustomerCustomAuthenticationProvider where each AuthenticationProvider should be overridden supports method where AuthenticationProvider supports specific token either customerAuthToken or employeeAuthToken.
Override authenticate method where authenticate method has been passed with Authentication parameter from which you can get both username and password which you can pass to any of you custom service to authenticate user and grant authorities required for user.
While implementing your CustomAuthenticationFilter it is also required to provide your custom authenticationSuccessHandler and AuthenticationFailureHandlers.
If you implement all above without any mistake you can avoid fallback authentication which spring-security provides by default if two customAuthenticationProviders are configured.
For more detail of implementing multiple authentication entry point using java configuration refer my answer given below
Multiple AuthenticationProvider with different UsernamePasswordAuthToken to authenticate different login forms without fallback authentication
and also you can download working code from my github repository
"Will there be two UserDetailsService and two AuthenticationProvider each pointing to its own table (Customer and Employee)?" ..... The answer is yes.
Spring security has filter, UsernamePasswordAuthenticationFilter (check the name of the filter)where you can implement the specific implementation based on input type.
I did the same thing but for different auth mechanisms.
But as per your requirement, it is possible what your looking for.
Related
I am new to Spring boot. I need to implement role based authorization in spring boot. I am having different roles and multiple users will be mapped to each role. I will be setting different access (read, add, delete, edit)
Whenever an api gets called, need to check the access and allow permission.
I am planning to use interceptor to call method having the query to get the access from DB and deny or access the api.
Is there any other better way I can use for the same?
If you are using Spring Security you can handle it with method security annotations like #PreAuthorize, #PostAuthorize .. even combine them to new annotations.
First your User need to implements UserDetails then you should implement getAuthorities() method according to your Role and Authority structure Spring Security basically checks what getAuthority() method returns if returned value prefixed with "ROLE_" like "ROLE_ADMIN" it will be processed as ROLE if it does not prefixed with "ROLE_" it will be processed as Authority
you can use method annotation for checking authority and role like following example:
#PreAuthorize("hasRole('ROLE_ADMIN') and hasAuthority("READ")")
and Spring Security will check your granted Authorities by getAuthorities() implementation of your User then, according to your annotation it will be checked automatically by Spring Security behalf of you.
For clarity you can check
https://www.baeldung.com/spring-security-granted-authority-vs-role
For quick working implementation you can check article below(I would not directly use it but you can understand the idea. Also you can use permissions but simple solution probably the solution below.):
https://www.baeldung.com/role-and-privilege-for-spring-security-registration
For authorization, there can be these two ways as well:
OAuth (Reference - https://medium.com/#bvulaj/mapping-your-users-and-roles-with-spring-boot-oauth2-a7ac3bbe8e7f)
Spring Security Roles and Privileges(Reference- https://www.baeldung.com/role-and-privilege-for-spring-security-registration)
You can create a custom annotation to handle request for each role. I you can read this article for more details about how to implement.
And in api will have format:
#GetMapping(...)
#YouCustomAnnotation("roleName")
public void doSomeThing(){
}
This api will be called if role of user matched with role define in annotation and server will return 404 code if user's role not match.
I have a Spring REST-API which is secured by SpringSecurity.
I have different ways to authenticate in that application.
Customer (db)
Employees (ldap)
Other Applications/Services (inMemory)
Each of these are covered by a different authentication provider, which properly assigns the access roles. (Works)
However I need to know which authentication provider was used to authenticate the user, so I can write it to the logs, history...
User CUSTOMER/max.pain created a new entry.
And of course the username does not really contain that prefix, neither in the db, nor in ldap.
I considered adding a special role based on the authentication provider used, but extracting that information over and over is annoying and probably has bad performance and might cause problems in the future. (user.getGrantedAuthorities().findStartingWith("AP_"))
I also tried using org.springframework.security.config.annotation.SecurityConfigurerAdapter.addObjectPostProcessor(ObjectPostProcessor<?>) but i cannot use it to wrap the AuthenticationProvider with a custom AuthenticationProvider class, because it requires the use the same class for whatever reason.
Is there an inbuild method I could use to achieve that or do I have to use a custom implementation/configuration?
Prozess steps:
Login: EMPLOYEE/always.calm
Don't check CUSTOMER AuthenticationProvider
Check EMPLOYEE AuthenticationProvider
Don't check SERVICE AuthenticationProvider
authentication.getPrincipal().getAP() = EMPLOYEE
in my project, I m using request.getUserPrincipal() to check whether user is logged in or not, but I am confused where to set the instance as logged in user?
and i also notice that request.getUserPrincipal().getName() return user login id .
Finally I found where is the place we set our own db user details to the spring security Principal, we have a class which implemented spring security AuthenticationProvider interface , and inside public Authentication authenticate(Authentication authentication) method, we set the value from USER_TB to Spring security UserDetails object. I am still investagating, spring security is a big part to learn
I'm using Spring Security to provide authentication and authorization to my webapp.
I use the standard method UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; to login with a DaoAuthenticationProvider.
Once successfully logged in, I can access the user's authorities in two different ways:
1# SecurityContextHolder.getContext().getAuthentication().getAuthorities()
2# ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getAuthorities()
They are supposed to be the same, as the Authentication instance authority list is constructed from the UserDetails' user.getAuthorities(), but they are in fact 2 different Collection instances. The principal's one is an UnmodifiableSet, the one in the Authentication instance is an UnmodifiableRandomAccessList.
2 different Collection instances that contain the same list of authority.
This struck me when trying to find a solution to dynamically add an authority to the currently authenticated Principal. It's not a good thing and the security layer should not allow me to do so, but still that's what I need.
A solution I found acceptable was to programmatically login with the currently authenticated user with
new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), dbAuthsSet);
authentication being the current authentication instance and dbAuthSet the current list of authorities + the one I need to add.
And this works fine. The only odd thing now is that
1# SecurityContextHolder.getContext().getAuthentication().getAuthorities()
2# ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getAuthorities()
don't return the same list of authorities anymore.
If I check authorities against the Authentication's list in my application, I'm good, and this is perfectly justified thing to do. But if I check against the Principal's list, I'm faced with the inconsistency.
What do you think? Is it a problem that should be addressed in Spring's Security 3.2?
Do you find it reasonable to programmatically login with new UsernamePasswordAuthenticationToken() to achieve my goal of giving an additional authority to the currently authenticated user for the session?
Would it be a good case to use Spring's Run-As Authentication Replacement feature?
Could anyone share experience on this ?
I need to make a legacy application start using spring security 3.
This app already has its security data model with:
Very simple by far. I can write my custom usersByUsernameQuery and authoritiesByUsernameQuery.
The thing is that there is another table indicating the operation (i.e. #Service layer method) that a Role can execute:
So the administrator can enable/disable a role from accessing an operation through a web interface, without redeploying the app.
I still can annotate the business methods with #Secure('ROLE_ADMIN') for example, but my custom UserDetailsService must know at least the method name that is being secured, so I can perform the right query.
So, the question is: is there a way that my custom UserDetailsService can intercept the method's name that is being secured?
It sounds like your access-decision is based on the "operation role", rather than the user roles, so it might be better to use the "operational role" directly in the Spring Security constraints. That is essentially an RBAC approach, where there is a mapping between the user roles and the operations they are allowed to perform.
You would address the issue in the AuthenticationProvider rather than the UserDetailsService, by adding a mapping layer in there which translates the user roles (supplied by the UserDetailsService) into the rights that the user has within the application. These would make up the collection of authorities that are returned in the Authentication object created by the AuthenticationProvider.
The mapping layer would directly use the data which your administration interface provides.
You might want to take a look at this presentation, by Mike Weisner, which covers similar material, amongst other things.
Not also that Spring Security 3.1 will include an additional GrantedAuthorityMapper strategy to make it easier to plug in a mapping of this kind.