I am using spring mvc3 to build a user-manager system.
This system conain the following models:
Department
User
And the Departments have the hierarchic structure,for example:
Dep1
SubDep1
SubDep2
Sub_sub_dep1
xxxx
One can add/update/delete departments/users if he is authenciationed,but he can only do these operation within his department and the sub-departmens.
For example,there are three departments(with there users):
Dep01(user1:{id:1}}
Dep0101(user2:{id:2}
Dep0102(user3:{id:3}
Dep010201(user4:{id:4}
So user1 can do the /add/upate/delete all the users(user1,user2,user3,user4)
While user3 can only do the operation for user(user3,user4).
I can control that the user3 can not see the user1 and user2 in the department/list page.
But how about if he enter the url like this:
department/update/1
This has to be avoided since the user1(whose id is 1) does not belong to Dep0102 or Dep010201.
How to controll this?
One option is to create a custom Spring Security PermissionEvaluator and implement your custom checks in the hasPermission(Authentication authentication, Object targetDomainObject, Object permission) method.
The signature of the method to protect ends up looking like this:
#PreAuthorize("hasRole('ROLE_USER') and hasPermission(#_dept, 'deptAndSubs')")
public String methodToProtect(String _dept)throws Exception {
<custom code>;
}
The first argument to the hasPermission expression is the department that the user wants to modify and the second is the permission. For us the deptAndSubs permission indicates that the user can execute the method only if the department being modified is equal to the users assigned department or any of the sub departments of that department (other permissions are 'deptOnly' and 'subsOnly').
In our application we have a custom Spring Security UserDetails object that includes the user department code so we can get the logged in user's department directly from the Authentication object that Spring passes into the method. Here's what the custom evaluator finally ends up looking like:
public class CustomPermissionEvaluator implements PermissionEvaluator {
#Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
AppUser appUser = (AppUser)authentication.getPrincipal();
if(permission instanceof String){
if("deptAndSubs".equals(permission)){
return isTargetDeptInUserDeptTree((String)targetDomainObject, appUser.getDeptCode());
}else if(.... other permission checks){}
}
return false;
}
The method isTargetDeptInUserDeptTree is custom code to extract the user's department tree and verify that the target department is in it.
Finally you have to set up your xml configuration:
<global-method-security pre-post-annotations="enabled" >
<expression-handler ref="expressionHandler"/>
</global-method-security>
<beans:bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<beans:property name="permissionEvaluator" ref="customPermissionEvaluator"/>
</beans:bean>
<beans:bean id="customPermissionEvaluator" class="....CustomPermissionEvaluator"/>
Good luck!
Related
I need to have authorization at the method level so that the users with proper permissions only can access it. The method will contain a token as a parameter. I need to make an API call passing the token and get the user email id. Once I have the email id, I need to fetch the user's roles & permissions from the database. Then I invoke the method if the user have appropriate roles else return a 403 error.
Is there a way to get this done in spring boot? I will have multiple methods behind authorization and would like to have some kind of annotation at method level.
Thanks.
#PreAuthorize annotation is what you want
Please read the following link for spring method level authorization
baeldung method authorization
you will also need to undestand SPEL(Spring Expression Language) as this is what the PreAuthorize method gets as parameter , link can be found here
please note that spring uses the SecurityContext to get the user data(Role etc..), meaning that the user already passed the login(authentication) stage and has SecurityContext loaded for said user
Example:
//other annotations
#PreAuthorize("hasRole('ROLE_VIEWER')") // hasRole('ROLE_VIEWER') -> this is SPEL
public ResponseEntity<String> methodName() {
//method
}
You can use #PreAuthorize with more flex as:-
#PreAuthorize("#securityService.hasPermission({'PERMISSION_1'})")
and service:-
#Component("securityService")
public class SecurityService {
public boolean hasPermission(PermissionEnum... permissions) {
Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication()
.getAuthorities();
for (PermissionEnum permission : permissions) {
if (authorities.contains(new SimpleGrantedAuthority(permission.toString))) {
return true;
}
}
return false;
}
}
You can make it as you want.
For more
https://dreamix.eu/blog/java/implementing-custom-authorization-function-for-springs-pre-and-post-annotations
https://try2explore.com/questions/10125443
I need to get current loggedin user's details along entertain session.Is there any poissible to do it in Spring and hibernate
I am new to spring and hibernate...Help me guys.
There are serveral ways in which you can accomplish this. In a controller you can inject a Pricipal as a parameter.
#RequestMapping()
public void getPricipal(Principal principal) {
Customer c = (Customer) pricipal;
}
You should also be able to inject the UserDetails directly as well. Spring will resolve it for you. Your Customer class must implement UserDetails
#RequestMapping()
public void getPricipal(Customer customer) {
}
As for having all details, you should be able to access the firstname, lastname etc, if you've implemented your own UserDetailsService where you return your custom UserDetails (Customer).
I'm trying to show no of invalid attempts of user using spring security.I'm using a custom User class to get additional user details other than username and password. I've created two listener classes i.e. AuthenticationSuccessEventListener & AuthenticationFailureListener to update user's invalid attempts.
Now in the onApplicationEvent method i'm trying to get custom User object (CustomUserDetails) like shown below:
#Component
public class AuthenticationFailureListener implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> {
#Autowired
private ILoginDAO loginDAO ;
#Override
public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent event) {
CustomUserDetails user = (CustomUserDetails)event.getAuthentication().getPrincipal();//I get ClassCastException here.
String strUserID = user.getUserID();
CustomUserDetails customUser = loginDAO.loadUserByUsername(strUserID);
if (customUser != null){
...
} } }
event.getAuthentication().getPrincipal() returns a String i.e. username which i'm trying to cast it to CustomUserDetails (custom User class) and i get error.
P.S - I'm entering userid/password in login page and hence i pass userid as parameter for all the methods including loadUserByUsername(strUserID).
How can i get my custom User object in the listener class from AuthenticationFailureBadCredentialsEvent / AuthenticationSuccessEvent object?
The event just contains the authentication request object, i.e. the Authentication which was passed to the AuthenticationManager and which failed. So it will contain the submitted username as the principal.
Authentication may have failed for a variety of reasons, including a non-existent username, and in fact doesn't even need to involve a UserDetails object at all, so if you want the full information you will need to load it using the username.
Alternatively you could customize an AuthenticationProvider to perform the additional work you want in the implementation itself, rather than via an event.
Currently I'm using spring security and #PreAuthorize annotations to secure method calls. Now I want to change the authentication token for a method call like the run-as authentication replacement of spring security allows me to do.
Can I configure the replacement on a per method base? Per annotation, SpEL expression....
If not, would it be possible do figure out in the runAsManager what method is called?
How would I configure the security config attributes for a secured object, at all?
I've posted a detailed article on implementing Run-As in conjunction with #PreAuthorize.
1) Implement your own RunAsManager that creates the Authentication to use during method execution based on any custom logic. The example below uses a custom annotation that provides the extra role:
public class AnnotationDrivenRunAsManager extends RunAsManagerImpl {
#Override
public Authentication buildRunAs(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
if(!(object instanceof ReflectiveMethodInvocation) || ((ReflectiveMethodInvocation)object).getMethod().getAnnotation(RunAsRole.class) == null) {
return super.buildRunAs(authentication, object, attributes);
}
String roleName = ((ReflectiveMethodInvocation)object).getMethod().getAnnotation(RunAsRole.class).value();
if (roleName == null || roleName.isEmpty()) {
return null;
}
GrantedAuthority runAsAuthority = new SimpleGrantedAuthority(roleName);
List<GrantedAuthority> newAuthorities = new ArrayList<GrantedAuthority>();
// Add existing authorities
newAuthorities.addAll(authentication.getAuthorities());
// Add the new run-as authority
newAuthorities.add(runAsAuthority);
return new RunAsUserToken(getKey(), authentication.getPrincipal(), authentication.getCredentials(),
newAuthorities, authentication.getClass());
}
}
This implementation will look for a custom #RunAsRole annotation on a protected method (e.g. #RunAsRole("ROLE_AUDITOR")) and, if found, will add the given authority (ROLE_AUDITOR in this case) to the list of granted authorities. RunAsRole itself is just a simple custom annotation.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface RunAsRole {
String value();
}
2) Instantiate the manager:
<bean id="runAsManager"
class="org.springframework.security.access.intercept.RunAsManagerImpl">
<property name="key" value="my_run_as_key"/>
</bean>
3) Register it:
<global-method-security pre-post-annotations="enabled" run-as-manager-ref="runAsManager">
<expression-handler ref="expressionHandler"/>
</global-method-security>
4) Example usage in a Controller:
#Controller
public class TransactionLogController {
#PreAuthorize("hasRole('ROLE_REGISTERED_USER')") //Authority needed to access the method
#RunAsRole("ROLE_AUDITOR") //Authority added by RunAsManager
#RequestMapping(value = "/transactions", method = RequestMethod.GET) //Spring MVC configuration. Not related to security
#ResponseBody //Spring MVC configuration. Not related to security
public List<Transaction> getTransactionLog(...) {
... //Invoke something in the backend requiring ROLE_AUDITOR
{
... //User does not have ROLE_AUDITOR here
}
EDIT:
The value of key in RunAsManagerImpl can be anything you want. Here's the excerpt from Spring docs on its use:
To ensure malicious code does not create a RunAsUserToken and present
it for guaranteed acceptance by the RunAsImplAuthenticationProvider,
the hash of a key is stored in all generated tokens. The
RunAsManagerImpl and RunAsImplAuthenticationProvider is created in the
bean context with the same key:
<bean id="runAsManager"
class="org.springframework.security.access.intercept.RunAsManagerImpl">
<bean id="runAsAuthenticationProvider"
class="org.springframework.security.access.intercept.RunAsImplAuthenticationProvider">
By using the
same key, each RunAsUserToken can be validated it was created by an
approved RunAsManagerImpl. The RunAsUserToken is immutable after
creation for security reasons.
I solved this by implementing my own RunAsManager that checks for a custom annotation on the invoked method and returns the appropriate Token.
works great.
I want to use Spring Security to manage user, group and permissions.
I want to use ACL to secure my domain objects but I can't find a way to assign a group to an acl.
For example:
I've got users and groups. Each group can have the following securities:
- manage forums (can be a role like ROLE_FORUM_MANAGER)
- edit a specific forum (acl on the specific forum).
Moreover, Groups are defined by users which have role ROLE_PERMISSION_MANAGER. BUT all groups defined by this user can only be edited and managed by this user. So group are attached to a user. Exactly, imagine that user creates a google group: this user can manage right permission groups only for the group he has created. And so he can create group to manage specific forum of its own google group.
How can I do it?
I read the spring security docs and the following tutorials (so please don't send me to these links):
http://grzegorzborkowski.blogspot.com/2008/10/spring-security-acl-very-basic-tutorial.html
http://blog.denksoft.com/?page_id=20
Check Spring Security 3.0, you might be able to avoid using ACL at all by using the Spring Expression Language.
For instance, for editing a forum, you would have a method secured like this:
#PreAuthorize("hasRole('ROLE_FORUM_MANAGER') and hasPermission(#forum,'update'))
public void updateForum(Forum forum) {
//some implementation
}
You would then implement the hasPermission method in a custom permission evaluator, like:
public class ForumPermissionEvaluator implements PermissionEvaluator {
public boolean hasPermission(Authentication authentication,
Object domainObject, Object permission) {
//implement
}
public boolean hasPermission(Authentication authentication,
Serializable targetId, String targetType, Object permission) {
//implement
}
}
Finally, wire it up together in the application config:
<beans:bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<beans:property name="permissionEvaluator" ref="permissionEvaluator"/>
</beans:bean>
<beans:bean id="permissionEvaluator"
class="x.y.z.ForumPermissionEvaluator" />
I would just use your Groups like Roles. I've found the Spring ACL implementation to be pretty unwieldy and for the most part unusable. Just assign users to "groups" (Roles in all actuality) and check them as you would normal role based authorization.
I did something similar 'manually': i.e. I had my own code to determine which instances could be edited/deleted by a specific user and only relied on Spring security to ensure they had the right role to access the functionality and to provide role/authentication information for the current user.
So in my code I determined the current principal (our own User class) and based on that I decided what rights this user had on a specific instance.
public static User getCurrentUser() {
User user = null;
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
Object principal = auth.getPrincipal();
if (principal instanceof User) {
user = (User)principal;
}
}
return user;
}