云e办(后端)——权限管理RABC

云e办(后端)——权限管理RABC

如果用户直接在地址上输入本身权限没有的URL,我们也应该对其控制。这样就设计到权限的概念。

思路:需要用户登录成功后,才会进行判断有无权限查看相应内容。

  • 1.根据请求的url判断角色,,根据角色进行判断能访问哪些资源(菜单列表)
  • 2.分析登录的用户有哪些角色,根据角色进行判断能访问哪些资源(菜单列表)

权限管理RABC

RBAC是基于角色的访问控制( Role-Based Access Control )在RBAC中,权限与角色相关联,用户通过扮演适当的角色从而得到这些角色的权限。这样管理都是层级相互依赖的,权限赋予给角色,角色又赋予用户,这样的权限设计很清楚,管理起来很方便。

RBAC授权实际上是 Who 、 What 、 How 三元组之间的关系,也就是 Who 对 What 进行 How 的操作,简单说明就是谁对什么资源做了怎样的操作。

本次项目中:
用户与角色实体对应关系为多对多
角色与资源对应关系同样为多对多关系
所以在实体设计上用户与角色间增加用户角色实体,将多对多的对应关系拆分为一对多,同理,角色与资源多对多对应关系拆分出中间实体对象权限实体

表结构设计

权限表设计分为以下基本的五张表结构:
用户表(admin),角色表(role),用户角色表(admin_role),菜单表(menu),菜单角色表(menu_role),
表结构关系如下:

在这里插入图片描述
多对多,会用到中间表。如果是一对多不需要中间表,用外键就可以。

根据请求的url判断角色,从角色中获取菜单

1.POJO类、Menu

在这里插入图片描述

2.server: IMenusServer
    /**
     * 根据角色获取菜单列表
     */
    List<Menu> getMenusWithRole();

    /**
     * 根据角色获取菜单列表
     */
    @Override
    public List<Menu> getMenusWithRole() {
        return menuMapper.getMenusWithRole();
    }

3.mapper: MenuMapper
    /**
     * 根据用户 获取菜单列表
     */
    List<Menu> getMenusWithRole();

    <resultMap id="MenusWithRole" type="com.xxxx.server.pojo.Menu" extends="BaseResultMap">
        <collection property="roles" ofType="com.xxxx.server.pojo.Role">
            <id column="rid" property="id"/>
            <result column="rname" property="name" />
            <result column="rnameZh" property="nameZh"/>
        </collection>
    </resultMap>

    <!--根据角色获取菜单列表-->
    <select id="getMenusWithRole" resultMap="MenusWithRole">
        SELECT
            m.*,
            r.id AS rid,
            r.`name` AS rname,
            r.nameZh AS rnameZH
        FROM
            t_menu m,
            t_menu_role mr,
            t_role r
        WHERE
            m.id=mr.mid
          and
            r.id=mr.rid
        ORDER BY m.id
    </select>


二、过滤器:判断menu的url有没有请求的URL,有则返回能请求该URL的角色列表

package com.xxxx.server.config.security.component;

import com.xxxx.server.pojo.Menu;
import com.xxxx.server.pojo.Role;
import com.xxxx.server.service.IMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.util.Collection;
import java.util.List;

/**
 * 权限控制
 * 根据请求URL 分析请求所需的角色
 */
@Component
public class CustomFilter implements FilterInvocationSecurityMetadataSource {
    @Autowired
    private IMenuService menuService;
    AntPathMatcher antPathMatcher =new AntPathMatcher();
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        // 获取请求的URL
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        //根据角色查询所有的菜单
        List<Menu> menus = menuService.getMenusWithRole();
        for (Menu menu : menus) {
            //判断请求URL与菜单中的URL能否匹配上。
            if(antPathMatcher.match(menu.getUrl(),requestUrl)){
                //如果能匹配上,则返回菜单URL所对应的所有角色是
                String[] strings = menu.getRoles().stream().map(Role::getName).toArray(String[]::new);
                //返回能访问该URL的所有角色
                return SecurityConfig.createList(strings);
            }
        }
        //没匹配的URL默认登录即可访问
        return SecurityConfig.createList("ROLE_LOGIN");
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return false;
    }
}


三、判断用户都有哪些角色列表 与 请求URL所需的角色,进行比较:一致则可以访问

1.修改管理员类,获取当前用户角色–pojo/Admin.java

在管理员类里添加角色列表属性,并且可以获取到当前用户的角色
pojo/Admin.java

     /**
     * 已经是SpringSecurity框架了
     * 真正登录的方法就是UserDetails的Username
     * 登陆成功就是details
     * @return
     */
    @ApiModelProperty(value = "权限")
    @TableField(exist = false)
    private List<Role> roles;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities =
                roles.stream()
                        .map(role -> new SimpleGrantedAuthority(role.getName()))
                        .collect(Collectors.toList());
        return authorities;
    }

2、Controller层,获取当前登录的用户id,传给service层…

LoginController.java

   /**
     * 获取当前登录的用户信息
     *
     * springsecurity将当前登录的对象设置到全局里面了:SecurityContextHolder.getContext().setAuthentication(authentication);
     * 可以通过principal获取当前登录的对象
     */

    @ApiOperation(value = "获取当前登录用户的信息")
    @GetMapping("/admin/info")
    public Admin getAdminInfo(Principal principal) {
        if (null == principal) {
            return null;
        }
        //principal 直接获取当前登录的对象
        String username = principal.getName(); //获取当前登录的用户名
        //根据当前的用户名去查询所有信息
        Admin admin = adminService.getAdminByUserName(username);
        //故意将密码返回空。
        admin.setPassword(null);
        //获取所有用户信息,并将角色也一起返回。角色表是角色表,没有在用户表当中。
        admin.setRoles(adminService.getRoles(admin.getId()));
        return admin;
    }

3.根据当前登录的用户id,查询他的角色权限列表–》AdminServiceImpl.java
/**
* 根据用户id或者权限列表
*
* @param adminId
* @return
*/
List<Role> getRoles(Integer adminId);
/**
* 根据用户id获取权限列表
*
* @param adminId
* @return
*/
@Override
public List<Role> getRoles(Integer adminId) {
   return roleMapper.getRoles(adminId);
}

4. RoleMapper
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, name, nameZh
    </sql>

    <!--根据用户id获取权限列表-->
    <select id="getRoles" resultType="com.xxxx.server.pojo.Role">
        SELECT
            r.id,
            r.`name`,
            r.nameZh
        FROM
            t_role AS r
                LEFT JOIN t_admin_role AS ar ON ar.rid = r.id
        WHERE
            ar.adminId = #{adminId}
    </select>

4、添加拦截器:判断登录用户所拥有的角色,是否为url所需角色

CustomUrlDecisionManager

package com.xxxx.server.config.security.component;

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * 权限控制
 * 判断用户角色
 *
 * @author zhoubin
 * @since 1.0.0
 */
@Component
public class CustomUrlDecisionManager implements AccessDecisionManager {

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        for (ConfigAttribute configAttribute : configAttributes) {
            //当前url所需角色
            String needRole = configAttribute.getAttribute();
            //判断角色是否登录即可访问的角色,此角色在CustomFilter中设置
            if ("ROLE_LOGIN".equals(needRole)){
                //判断是否登录
                if (authentication instanceof AnonymousAuthenticationToken){
                    throw new AccessDeniedException("尚未登录,请登录!");
                }else {
                    return;
                }
            }
            //判断用户角色是否为url所需角色
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                if (authority.getAuthority().equals(needRole)){
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足,请联系管理员!");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

5、security动态权限配置
//动态权限配置
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
    @Override
    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
        object.setAccessDecisionManager(customUrlDecisionManager);
        object.setSecurityMetadataSource(customFilter);
        return object;
    }
})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

真真最可爱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值