美文网首页
grails+security细粒度权限

grails+security细粒度权限

作者: Toplhyi | 来源:发表于2019-10-03 22:17 被阅读0次

grails项目中使用了spring security 插件实现身份认证。插件提供了一些标签来在页面中进行权限的判断,例如:ifLoggedIn、ifAllGranted、ifAnyGranted、ifNotGranted、sec:access等,但是基本上都是基于角色来进行验证的。
这次需求是要控制到页面上的每个按钮,而按钮及菜单的权限关联在角色上,角色是用户动态创建的,所以这种基于角色的验证是无法满足需求的。
所以打算这么来做:

  1. 自定义UserDeatils类,添加menus属性

import grails.plugin.springsecurity.userdetails.GrailsUser
import org.springframework.security.core.GrantedAuthority

class CustomUser extends GrailsUser{

    private Set<String> menus;
    /**
     * Constructor.
     *
     * @param username the username presented to the
     *        <code>DaoAuthenticationProvider</code>
     * @param password the password that should be presented to the
     *        <code>DaoAuthenticationProvider</code>
     * @param enabled set to <code>true</code> if the user is enabled
     * @param accountNonExpired set to <code>true</code> if the account has not expired
     * @param credentialsNonExpired set to <code>true</code> if the credentials have not expired
     * @param accountNonLocked set to <code>true</code> if the account is not locked
     * @param authorities the authorities that should be granted to the caller if they
     *        presented the correct username and password and the user is enabled. Not null.
     * @param id the id of the domain class instance used to populate this
     */
    CustomUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<GrantedAuthority> authorities, Object id, Set<String> menus) {
        super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, id)
        this.menus = menus;
    }
}

  1. 自定义CustomUserDetailsService, 在认证成功后封装menus

import grails.core.GrailsApplication
import grails.gorm.transactions.Transactional
import grails.plugin.springsecurity.SpringSecurityUtils
import grails.plugin.springsecurity.userdetails.GrailsUserDetailsService
import grails.plugin.springsecurity.userdetails.NoStackUsernameNotFoundException
import groovy.util.logging.Slf4j
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UsernameNotFoundException

@Slf4j
class CustomUserDetailsService implements GrailsUserDetailsService{

    /**
     * Some Spring Security classes (e.g. RoleHierarchyVoter) expect at least one role, so
     * we give a user with no granted roles this one which gets past that restriction but
     * doesn't grant anything.
     */
    static final GrantedAuthority NO_ROLE = new SimpleGrantedAuthority(SpringSecurityUtils.NO_ROLE)

    /** Dependency injection for the application. */
    GrailsApplication grailsApplication


    @Transactional(readOnly=true, noRollbackFor=[IllegalArgumentException, UsernameNotFoundException])
    UserDetails loadUserByUsername(String username, boolean loadRoles) throws UsernameNotFoundException {
        def conf = SpringSecurityUtils.securityConfig
        String userClassName = conf.userLookup.userDomainClassName
        def dc = grailsApplication.getDomainClass(userClassName)
        if (!dc) {
            throw new IllegalArgumentException("The specified user domain class '$userClassName' is not a domain class")
        }

        Class<?> User = dc.clazz

        def user = User.createCriteria().get {
            if(conf.userLookup.usernameIgnoreCase) {
                eq((conf.userLookup.usernamePropertyName), username, [ignoreCase: true])
            } else {
                eq((conf.userLookup.usernamePropertyName), username)
            }
        }

        if (!user) {
            log.warn 'User not found: {}', username
            throw new NoStackUsernameNotFoundException()
        }

        Collection<GrantedAuthority> authorities = loadAuthorities(user, username, loadRoles)
        Set<String> menus = loadMenus(user)
        createUserDetails user, authorities, menus
    }

    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        loadUserByUsername username, true
    }

    protected Collection<GrantedAuthority> loadAuthorities(user, String username, boolean loadRoles) {
        if (!loadRoles) {
            return []
        }

        def conf = SpringSecurityUtils.securityConfig

        String authoritiesPropertyName = conf.userLookup.authoritiesPropertyName
        String authorityPropertyName = conf.authority.nameField

        boolean useGroups = conf.useRoleGroups
        String authorityGroupPropertyName = conf.authority.groupAuthorityNameField

        Collection<?> userAuthorities = user."$authoritiesPropertyName"
        def authorities

        if (useGroups) {
            if (authorityGroupPropertyName) {
                authorities = userAuthorities.collect {
                    it."$authorityGroupPropertyName"
                }.flatten().unique().collect {
                    new SimpleGrantedAuthority(it."$authorityPropertyName")
                }
            }
            else {
                log.warn 'Attempted to use group authorities, but the authority name field for the group class has not been defined.'
            }
        }
        else {
            authorities = userAuthorities.collect {
                new SimpleGrantedAuthority(it."$authorityPropertyName")
            }
        }
        authorities ?: [NO_ROLE]
    }

    protected Set<String> loadMenus(user) {
        def conf = SpringSecurityUtils.securityConfig
        String authoritiesPropertyName = conf.userLookup.authoritiesPropertyName
        Collection<?> userAuthorities = user."$authoritiesPropertyName"
        def menus = []
        userAuthorities.collect {
            menus += it?.menus?.url
        }
        menus?.removeAll(Collections.singleton(null))
        new HashSet<String>(menus)
    }

    protected UserDetails createUserDetails(user, Collection<GrantedAuthority> authorities, Set<String> menus) {

        def conf = SpringSecurityUtils.securityConfig

        String usernamePropertyName = conf.userLookup.usernamePropertyName
        String passwordPropertyName = conf.userLookup.passwordPropertyName
        String enabledPropertyName = conf.userLookup.enabledPropertyName
        String accountExpiredPropertyName = conf.userLookup.accountExpiredPropertyName
        String accountLockedPropertyName = conf.userLookup.accountLockedPropertyName
        String passwordExpiredPropertyName = conf.userLookup.passwordExpiredPropertyName

        String username = user."$usernamePropertyName"
        String password = user."$passwordPropertyName"
        boolean enabled = enabledPropertyName ? user."$enabledPropertyName" : true
        boolean accountExpired = accountExpiredPropertyName ? user."$accountExpiredPropertyName" : false
        boolean accountLocked = accountLockedPropertyName ? user."$accountLockedPropertyName" : false
        boolean passwordExpired = passwordExpiredPropertyName ? user."$passwordExpiredPropertyName" : false


        new CustomUser(username, password, enabled, !accountExpired, !passwordExpired,
                !accountLocked, authorities, user.id, menus)
    }
}

  1. 将自定义的CustomUserDetailsService注入到spring中,覆盖掉原有的userDetailsService
import com.rici.cas.center.core.BaseUserPasswordEncoderListener
import com.rici.cas.center.security.CustomUserDetailsService

// Place your Spring DSL code here
beans = {
    userDetailsService(CustomUserDetailsService){
        grailsApplication = ref("grailsApplication")
    }
}

  1. 自定义标签
import org.springframework.security.core.Authentication

class PermissionTagLib {
    static defaultEncodeAs = [taglib:'html']
    //static encodeAsForTags = [tagName: [taglib:'html'], otherTagName: [taglib:'none']]
    static namespace = 'per'

    def springSecurityService

    def isAllowed = { attrs, body ->
        // 获取到用户
        Authentication auth = springSecurityService.authentication
        if(auth){
            // 获取到用户能访问的菜单集合
            def menus = auth?.userDetails?.menus
            // 判断此url是否在在集合中
            def url = attrs.url
            if(!url){
                throwTagError("Tag isAllowed requires a url")
            }
            if(menus?.contains(url)){
                out << body()
            }
        }

    }
}
  1. 使用标签进行验证
<per:isAllowed url="/baseUser/index" >
    Hello World!!!!!
</per:isAllowed>

相关文章

网友评论

      本文标题:grails+security细粒度权限

      本文链接:https://www.haomeiwen.com/subject/mohjpctx.html