简体   繁体   中英

How to implement Custom spring security acl?

I'm developing an Application using Spring. In Access Control Access section I want to use Spring Security Acl (I'm new at Acl). I want to implement ACL in my application base on 2 points:

  1. Application should has five permissions, read , create , modify , delete and Administrator .
  2. Permissions be hierarchy, When a user has create permission, it should be able to read , Or when it has modify permission, it should be able to read , create and modify and etc.

Is it possible? How?

update :
My application is base on Spring MVC RESTFUL. When a user wants to modify own information, he send some json data with Ajax. An example of json data is as follow:

{
  "id": 1,//user Id
  "name": "my name",
  "password": "my password",
  "email": "email@email.com",
   ...
}

Now, a malicious user can login to own account. This user can modify its data like all other users. Before he send data, change his id, and modify another account user information. I want to use ACL to prevent this Subversive work. And user can get some access to other that others can modify his info.

You can implement a simple solution with spring security. The idea is to create a class that implements org.springframework.security.access.PermissionEvaluator and override the method hasPermission . Look the next example:

@Component("permissionEvaluator")
public class PermissionEvaluator implements org.springframework.security.access.PermissionEvaluator {

    /**
     * @param authentication     represents the user in question. Should not be null.
     * @param targetDomainObject the domain object for which permissions should be
     *                           checked. May be null in which case implementations should return false, as the null
     *                           condition can be checked explicitly in the expression.
     * @param permission         a representation of the permission object as supplied by the
     *                           expression system. Not null.
     * @return true if the permission is granted, false otherwise
     */
    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
        if (authentication != null && permission instanceof String) {
            User loggedUser = (User) authentication.getPrincipal();
            String permissionToCheck = (String) permission;
            // in this part of the code you need to check if the loggedUser has the "permission" over the
            // targetDomainObject. In this implementation the "permission" is a string, for example "read", or "update"
            // The targetDomainObject is an actual object, for example a object of UserProfile class (a class that
            // has the profile information for a User)

            // You can implement the permission to check over the targetDomainObject in the way that suits you best
            // A naive approach:
            if (targetDomainObject.getClass().getSimpleName().compareTo("UserProfile") == 0) {
                if ((UserProfile) targetDomainObject.getId() == loggedUser.getId())
                    return true;
            }
            // A more robust approach: you can have a table in your database holding permissions to each user over
            // certain targetDomainObjects
            List<Permission> userPermissions = permissionRepository.findByUserAndObject(loggedUser,
                targetDomainObject.getClass().getSimpleName());
            // now check if in userPermissions list we have the "permission" permission.

            // ETC...
        }
        //access denied
        return false;
    }

}

Now, with this implementation you can use in for example your service layer the @PreAuthorize annotation like this:

@PreAuthorize("hasPermission(#profile, 'update')")
public void updateUserProfileInASecureWay(UserProfile profile) {
    //code to update user profile
}

The "hasPermission" inside the @PreAuthorize annotation receives the targetDomainObject #profile from the params of the updateUserProfileInASecureWay method and also we pass the required permission (in this case 'update').

This solution avoids all the complexity of the ACL by implementing a "small" ACL. Maybe it can work for you.

You can use the role hierarchy combination as used in the following examples.Spring acl has base permissions,If you want to implement Custom permissions you need to create a custom Permission class extending Base Permissions class in the api. we can define roles for each kind of permission and define role hierarchy as below.

<bean id="expressionHandler"
    class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
    <property name="permissionEvaluator" ref="permissionEvaluator" />
    <property name="roleHierarchy" ref="roleHierarchy" />
</bean>

<!-- A customized PermissionEvaluator that evaluates permissions via the ACL module -->
<bean class="org.springframework.security.acls.AclPermissionEvaluator" id="permissionEvaluator">
    <!-- Reference to the ACL service which performs JDBC calls to an ACL database -->
    <constructor-arg ref="aclService"/>
     <property name="permissionFactory" ref="customPermissionFactory" /> 
</bean>
<bean id="customPermissionFactory" class="org.krams.tutorial.security.CustomPermissionFactory"></bean>

<!-- A customized ACL service which provides default JDBC implementation -->
<bean class="org.springframework.security.acls.jdbc.JdbcMutableAclService" id="aclService">
    <constructor-arg ref="dataSource"/>
    <constructor-arg ref="lookupStrategy"/>
    <constructor-arg ref="aclCache"/>
</bean>

<!-- A lookup strategy for optimizing database queries -->
<bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
    <constructor-arg ref="dataSource"/>
    <constructor-arg ref="aclCache"/>
    <constructor-arg ref="aclAuthorizationStrategy"/>
    <constructor-arg ref="auditLogger"/>
    <property name="permissionFactory" ref="customPermissionFactory"/>
</bean>

<!-- A MySQL datasource with pooling capabalities for the ACL module -->
<!-- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
     destroy-method="close"
     p:driverClass="com.mysql.jdbc.Driver"
     p:jdbcUrl="jdbc:mysql://localhost/acl"
     p:user="root"
     p:password=""
     p:acquireIncrement="5"
     p:idleConnectionTestPeriod="60"
     p:maxPoolSize="100"
     p:maxStatements="50"
     p:minPoolSize="10" /> -->




<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.h2.Driver" />
    <property name="url" value="jdbc:h2:tcp://localhost/~/springsecurity" />

    <!--<property name="url" value="jdbc:h2:tcp://ggk-wrl-win1/~/dev" /> -->
    <property name="username" value="sa" />
    <property name="password" value="" />
</bean>

<!-- An ACL cache to minimize calls to the ACL database -->   
<bean id="aclCache" class="org.springframework.security.acls.domain.EhCacheBasedAclCache">
    <constructor-arg>
        <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
            <property name="cacheManager">
                <bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
            </property>
            <property name="cacheName" value="aclCache"/>
        </bean>
    </constructor-arg>
</bean>

<!-- An ACL authorization strategy to determine whether a principal is permitted to call administrative methods -->
<bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
    <constructor-arg>
        <list>
            <bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
                <constructor-arg value="ROLE_USER"/>
            </bean>
            <bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
                <constructor-arg value="ROLE_USER"/>
            </bean>
            <bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
                <constructor-arg value="ROLE_USER"/>
            </bean>
        </list>
    </constructor-arg>
</bean>

<!-- An audit logger used to log audit events -->
<bean id="auditLogger" class="org.springframework.security.acls.domain.ConsoleAuditLogger"/>

<!-- Defines the role order -->
<!-- http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/access/hierarchicalroles/RoleHierarchyImpl.html -->
<bean id="roleHierarchy"  class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
    <property name="hierarchy">
        <value>
            ROLE_ADMIN > ROLE_USER
            ROLE_USER > ROLE_VISITOR
        </value>
    </property>
</bean>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM