Loading

没有角色表的权限管理,动态加载用户权限菜单(改造Spring security)

spring security

传统的spring security权限结构涉及到五张表

- 用户表

- 角色表

- 用户角色表

- 系统资源表

- 角色资源表  

给用户分配不同的角色,就可以动态加载该角色下的权限菜单。

 

新需求

不是根据角色进行菜单权限的分配,而是在用户注册后,用户进行自行勾选需要的权限菜单(根据不同的菜单选择,进行不同的缴费),也就是说用户自己选择需要开通哪些菜单的权限,弱化了用户角色的概念。

因此,对sprIng security进行改造

设计三张表

- 用户表 (记录注册的用户信息)

- 系统菜单资源表(记录所有的菜单信息)

 

 - 用户资源表(记录用户自己勾选的菜单权限,可更新)

 

 

设计思路及代码

用户在注册之后,只有默认的几个菜单可操作,对于没有开通的菜单没有操作权限。

所以在用户注册完之后,先进行所需要权限的开通。

@Data
public class AddUserPermissionVO
{

    @ApiModelProperty(value = "用户ID")
    private String userId;

    @ApiModelProperty(value = "用户所选权限列表", notes = "传过来的是路径地址list")
    private List<String> permissionLists;
}

如果是第一次进行菜单权限开通,则进行用户资源表的新增操作,如果是后续想开通其他的菜单权限,则进行更新操作。使用加密算法进行tokenId的加解密。

/**
     * 用户菜单权限添加
     * @param param
     */
    public void addUserPermission(AddUserPermissionVO param)
    {
        SysUserPermission model = new SysUserPermission();
        List<String> permissionLists = param.getPermissionLists();
        // 把用户id也封装进token
        permissionLists.add(param.getUserId());
        Map<String, Object> payload = new HashMap<>();
        // 把用户权限菜单封装成key
        payload.put(SIGNING_KEY, permissionLists);
        String token = JwtUtil.createToken(payload, EXP);

        model.setId(param.getUserId());
        model.setUserPermission(token);
        SysUserPermission user = sysUserPermissionService.findById(param.getUserId());
        if (CommonUtil.isEmpty(user))
        {
            sysUserPermissionService.add(model);
        } else
        {
            sysUserPermissionService.update(model);
        }
    }

添加完用户权限之后,用户每一次登录,都给前端返回用户资源表里的tokenId,前端根据返回的tokenId里的资源菜单路径信息,进行动态加载用户可操作的菜单。

SysUserPermission sysUserPermission = sysUserPermissionService.findById(oper.getOperatorId());
String tokenId = sysUserPermission.getUserPermission();
LoginResp loginResp = new LoginResp();
// 将tokenId返回给web页面,好做后续权限比对
loginResp.setTokenId(tokenId);
return loginResp;

做到这一步,前端已经能够动态展示用户可看到可操作的权限菜单列表了。但是,没有做到权限的控制(即前端的不可信任性),用户如果知道其他菜单的路径也能进行操作。

在spring security 里面,在后端的方法接口上添加角色的注解,根据用户的角色进行接口操作的控制。

因此,使用AOP横切过滤的方式,再次改造sprIng security,使用注解的形式,进行用户菜单权限的控制。

 

前端的不可信任,后端权限控制改造

/**
 * 权限自定义注解
 * 
 * @author LH
 * @date 2022/1/11 11:26
 */
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface UserPermission
{ String value()
default ""; }

AOP横切过滤

@Component
@Aspect
public class PermissionFilter
{
    // 自定义加密密钥SIGNING_KEY
    private static final String SIGNING_KEY = "你的加密key(自定义)";

    /**
     * 定义切点
     * @annotation 以注解作为切点,参数为注解定义路径
     */
    @Pointcut("@annotation(com.xxx.security.annotation.UserPermission)")
    private void permissionAspect()
    {
    }

    /**
     * @Before 方法执行前校验, permissionAspect()是切点,@annotation(userPermission)是注解参数,point是切点对象
     * @param userPermission
     * @return
     */
    @Before(value = "permissionAspect()&&@annotation(userPermission)")
    public Object checkPermissionkey(UserPermission userPermission)
    {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();

        // 得到tokenId并进行比对
        String tokenId = request.getHeader("X-Token");
        if (CommonUtil.isEmpty(tokenId))
        {
            // 这里还要加一层判断,当没有登录的用户进行操作时,没有tokenId,设置一个默认的default tokenId

            throw new RuntimeException("用户无访问权限!");
        }
        JwtUtil.parseToken(tokenId);
        // 这里进行分割,去掉路径前缀,匹对后面的请求路径
        String str = StrUtil.removeAll(JwtUtil.parseToken(tokenId).get(SIGNING_KEY).toString(), '[', ']');
        List<String> keyLists = BwFunc.getListSplitStr(str, ",");
        // userPermission.value()是注解参数
        if (keyLists.contains(userPermission.value()))
        {
            return null;
        } else
        {
            throw new RuntimeException("用户无访问权限!");
        }
    }
}

在接口方法上添加自定义注解进行测试

  @RequestMapping(value = "/addUser", method = RequestMethod.POST)
    @ApiOperation(value = "用户添加", notes = "用户添加", httpMethod = "POST")
    @UserPermission("addUser") // 只有在用户携带的token里面携带了 addUser参数,才能够对改接口方法进行后续的操作,否则返回用户无操作权限
    public BwResult addUserPermission(@RequestBody AddUserVO param)
    {
        sysUserPermissionAppService.addUser(param);
        return BwResult.success("用户添加成功!");
    }

到此,简单demo改造完成,后续看是否可进行优化。

posted @ 2022-01-11 16:40  你比从前快乐;  阅读(558)  评论(0编辑  收藏  举报