初出茅庐.开胃菜·代码思路·接口资源鉴权

YangeIT大约 7 分钟

初出茅庐.开胃菜·代码思路·接口资源鉴权

在看接口资源鉴权这部分代码的时候需要带着一些问题来看代码

  • 1.什么是接口资源
  • 2.什么是接口资源鉴权
  • 3.如何配置接口资源
  • 4.系统中的接口资源是和什么绑定的(用户,角色,部门?)
  • 5.CRM系统架构中是如何实现接口资源鉴权的

思路 🍐

  1. 先了解什么是接口资源(百度)
  2. 了解什么是接口资源鉴权(百度)
  3. 了解接口资源是和什么绑定的(查看产品原型或问产品经理(老师)),了解需求的过程可以询问产品经理
  4. 如果自己要实现接口资源鉴权如何实现
  5. CRM系统中的接口资源鉴权是如何实现的

什么是接口资源?API接口

用户哪些接口是能看到的,有哪些接口是能访问的,那么这些用户能访问的接口就称之为这个用户的接口资源,如果该用户没有对应的接口,后端应该返回403(没有权限)

简而言之:就是用户能看的、能操作的称之为 用户的接口资源,如果用户不能访问这个资源,应该返回403

思路:🍐

如果我要实现,对应的我应该知道,这些数据是还是存储在中的,如何进行配置,如何修改,然后前后端是如何交互的

1.页面配置

1. 系统中添加资源

系统管理--权限管理--菜单管理

可以配置对应的目录,目录下对应的接口

如果后续添加了新的功能,可以在这里配置目录和接口

2. 角色配置资源

在系统管理--权限管理--角色管理目录下可以点击对应角色的修改

点击修改可以修改该角色拥有的接口资源

⚠️注意:这里的权限标识符需要和代码中的权限标识符进行对应

可以添加对应的资源目录

2.关联数据库表

目录表:sys_menu

角色表:sys_role

角色和目录关联表:sys_role_menu

3.具体实现:

前端获取接口资源

首先在登录的时候回应该返回给前端这个用户具有哪些接口资源

这里很多同学会进行猜测,登录的时候,返回的,我们是做技术的,不要盲目猜测,通过前端,通过浏览器的F12看一下就知道这部分接口资源是如何获取的了

我们知道我们在登录的时候主要返回的是token,而token中并没有保存这部分接口资源信息

那么这部分的信息应该什么时候获取呢?

我们查看登录页面登录一次

可以看到在调用登录接口后又调用了一个getInfo接口,我们查看一下这个接口

我们在后端查看一下这个接口/getInfo

SysLoginController

    /**
     * 获取用户信息
     * 
     * @return 用户信息
     */
    @GetMapping("getInfo")
    public AjaxResult getInfo() {
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        SysUser user = loginUser.getUser();
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(user);
        // 权限集合
        Set<String> permissions = permissionService.getMenuPermission(user);
        AjaxResult ajax = AjaxResult.success();
        ajax.put("user", user);
        ajax.put("roles", roles);
        ajax.put("permissions", permissions);
        return ajax;
    }

首先getInfo接口是在登录接口之后调用的

所以通过request可以获取对应的用户

通过service查询对应的角色和权限集合将其保存在结果集中返回给前端,这样前端就拥有了对应的角色和接口资源,前端会根据对应的权限标识符来判断用户能看到哪些按钮,哪些目录,具体前端怎么判断的作为后端工程师的你不用关心。

后端接口资源鉴权

利用的是Security里的一个注解@PreAuthorize

这个注解是加载Controller层的需要接口权限的接口上的,对于公共的接口人人都可以访问的接口就不需要加这个注解了

	/**
	 * 查询线索管理列表
	 */
	@PreAuthorize("@ss.hasPermi('clues:clue:list')")
	@GetMapping("/list")
	public TableDataInfo list(TbClue tbClue) {
		...
	}

在controller层的接口上加上了@PreAuthorize("@ss.hasPermi('clues:clue:list')")这个注解

这个注解里的内容是ss.hasPermi('xxx:xxx:xx')

具体的实现要看PermissionService

PermissionService

package com.huike.framework.web.service;

import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.huike.common.core.domain.entity.SysRole;
import com.huike.common.core.domain.model.LoginUser;
import com.huike.common.utils.ServletUtils;
import com.huike.common.utils.StringUtils;

/**
 * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母
 * 
 * 
 */
@Service("ss")
public class PermissionService {
    /** 所有权限标识 */
    private static final String ALL_PERMISSION = "*:*:*";

    /** 管理员角色权限标识 */
    private static final String SUPER_ADMIN = "admin";

    private static final String ROLE_DELIMETER = ",";

    private static final String PERMISSION_DELIMETER = ",";

    @Autowired
    private TokenService tokenService;

    /**
     * 验证用户是否具备某权限
     * 
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    public boolean hasPermi(String permission) {
        if (StringUtils.isEmpty(permission)) {
            return false;
        }
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
            return false;
        }
        return hasPermissions(loginUser.getPermissions(), permission);
    }

    /**
     * 验证用户是否不具备某权限,与 hasPermi逻辑相反
     *
     * @param permission 权限字符串
     * @return 用户是否不具备某权限
     */
    public boolean lacksPermi(String permission)
    {
        return hasPermi(permission) != true;
    }

    /**
     * 验证用户是否具有以下任意一个权限
     *
     * @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表
     * @return 用户是否具有以下任意一个权限
     */
    public boolean hasAnyPermi(String permissions)
    {
        if (StringUtils.isEmpty(permissions))
        {
            return false;
        }
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
        {
            return false;
        }
        Set<String> authorities = loginUser.getPermissions();
        for (String permission : permissions.split(PERMISSION_DELIMETER))
        {
            if (permission != null && hasPermissions(authorities, permission))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断用户是否拥有某个角色
     * 
     * @param role 角色字符串
     * @return 用户是否具备某角色
     */
    public boolean hasRole(String role)
    {
        if (StringUtils.isEmpty(role))
        {
            return false;
        }
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
        {
            return false;
        }
        for (SysRole sysRole : loginUser.getUser().getRoles())
        {
            String roleKey = sysRole.getRoleKey();
            if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role)))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 验证用户是否不具备某角色,与 isRole逻辑相反。
     *
     * @param role 角色名称
     * @return 用户是否不具备某角色
     */
    public boolean lacksRole(String role)
    {
        return hasRole(role) != true;
    }

    /**
     * 验证用户是否具有以下任意一个角色
     *
     * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
     * @return 用户是否具有以下任意一个角色
     */
    public boolean hasAnyRoles(String roles)
    {
        if (StringUtils.isEmpty(roles))
        {
            return false;
        }
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
        {
            return false;
        }
        for (String role : roles.split(ROLE_DELIMETER))
        {
            if (hasRole(role))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否包含权限
     * 
     * @param permissions 权限列表
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    private boolean hasPermissions(Set<String> permissions, String permission)
    {
        return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
    }
}

可以看到的是我们定义了一个Serivice并且声明了一个在IOC容器中的别名叫ss

在@PreAuthorize("@ss.hasPermi('clues:clue:list')")这个注解中的@ss其实就是从ioc容器中找到叫ss的类

调用里面的hasPermi方法

这个方法代码如下

    /**
     * 验证用户是否具备某权限
     * 
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    public boolean hasPermi(String permission) {
        if (StringUtils.isEmpty(permission)) {
            return false;
        }
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) {
            return false;
        }
        return hasPermissions(loginUser.getPermissions(), permission);
    }

   /**
     * 判断是否包含权限
     * 
     * @param permissions 权限列表
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    private boolean hasPermissions(Set<String> permissions, String permission)
    {
        return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
    }

其中获取用户信息的部分:

TokenService

    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUser getLoginUser(HttpServletRequest request) {
        // 获取请求携带的令牌
        String token = getToken(request);
        if (StringUtils.isNotEmpty(token)) {
            Claims claims = parseToken(token);
            // 解析对应的权限以及用户信息
            String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
            String userKey = getTokenKey(uuid);
            LoginUser user = redisCache.getCacheObject(userKey);
            return user;
        }
        return null;
    }
    /**
     * 获取请求token
     *
     * @param request
     * @return token
     */
    private String getToken(HttpServletRequest request)
    {
        String token = request.getHeader(header);
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
        {
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
    }

根据用户里的权限列表集合和注解里的权限标识符进行比对

hasPermissions(loginUser.getPermissions(), permission);
 /**
     * 判断是否包含权限
     * 
     * @param permissions 权限列表
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    private boolean hasPermissions(Set<String> permissions, String permission)
    {
        return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
    }

如果包含了权限标识符则表示用户具有权限