权限管理与jwt鉴权=1

简介: 权限管理与jwt鉴权=1

权限管理与jwt鉴权

学习目标:

理解权限管理的需求以及设计思路实现角色分配和权限分配

理解常见的认证机制

能够使用JWT完成微服务Token签发与验证



权限管理

需求分析

完成权限(菜单,按钮(权限点),API接口)的基本操作



9a11d8b8efce218a6d3ce866a2e092ec.png




权限与菜单,菜单与按钮,菜单与API接口都是一对一关系。为了方便操作,在SAAS-HRM系统的表设计中,采用基于共享主键的形式实现一对一关系维护,并且数据库约束,一切的关系维护需要程序员在代码中实现。


后端实现

实体类

在系统微服务中创建权限,菜单,按钮(权限点),API对象的实体类


权限实体类Permission

package com.ihrm.domain.system;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
@Entity
@Table(name = "pe_permission")
@Getter
@Setter
@NoArgsConstructor
@DynamicInsert(true)
@DynamicUpdate(true)
public class Permission implements Serializable {
    private static final long serialVersionUID = -4990810027542971546L;
    /**
     * 主键
     */
    @Id
    private String id;
    /**
     * 权限名称
     */
    private String name;
    /**
     * 权限类型 1为菜单 2为功能 3为API
     */
    private Integer type;
    /**
     * 权限编码
     */
    private String code;
    /**
     * 权限描述
     */
    private String description;
    //父权限
    private String pid;
    //是否查询全部权限 0 :查询全部,1 :只查询企业所属权限
    private Integer enVisible;
    public Permission(String name, Integer type, String code, String description) {
        this.name = name;
        this.type = type;
        this.code = code;
        this.description = description;
    }
}


骚戴理解:pid是指的父权限,就是我菜单权限下面可能有一套按钮或者API,那么这些就是子权限,那么它们的父权限就是这个菜单的id。enVisible表示是否查询全部权限(0 :查询全部,1 :只查询企业所属权限)也就是0表示更加高级的权限,连企业都访问不了的,只有超级管理员可以看到的权限


权限菜单(权限点)实体类PermissionPoint

@Entity
@Table(name = "pe_permission_menu")
@Getter
@Setter
public class PermissionMenu implements Serializable {
    private static final long serialVersionUID = -1002411490113957485L;
    @Id
    private String id; //主键
    private String menuIcon; //展示图标
    private String menuOrder; //排序号
}


  • 权限菜单(权限点)实体类 PermissionPoint
@Entity
@Table(name = "pe_permission_point")
@Getter
@Setter
public class PermissionPoint implements Serializable {
    private static final long serialVersionUID = -1002411490113957485L;
    @Id
    private String id;
    private String pointClass;
    private String pointIcon;
    private String pointStatus;
}


  • 权限API实体类 PermissionApi
@Entity
@Table(name = "pe_permission_api")
@Getter
@Setter
public class PermissionApi implements Serializable {
    private static final long serialVersionUID = -1803315043290784820L;
    @Id
    private String id;
    private String apiUrl;
    private String apiMethod;
    private String apiLevel;//权限等级,1为通用接口权限,2为需校验接口权限
}

持久层

  • 权限持久化类
/**
  * 权限数据访问接口
  */
public interface PermissionDao extends JpaRepository<Permission, String>, 
JpaSpecificationExecutor<Permission> {
    List<Permission> findByTypeAndPid(int type,String pid);
}



  • 权限菜单持久化类
public interface PermissionMenuDao extends JpaRepository<PermissionMenu, String>, 
JpaSpecificationExecutor<PermissionMenu> {
}1. pub



权限按钮(点)持久化类

public interface PermissionPointDao extends JpaRepository<PermissionPoint, String>, JpaSpecificationExecutor<PermissionPoint> {
}


  • 权限API持久化类
public interface PermissionApiDao extends JpaRepository<PermissionApi, String>, JpaSpecificationExecutor<PermissionApi> {
}


业务逻辑

1. papackage com.ihrm.system.service;
import com.ihrm.common.entity.ResultCode;
import com.ihrm.common.exception.CommonException;
import com.ihrm.common.utils.BeanMapUtils;
import com.ihrm.common.utils.IdWorker;
import com.ihrm.common.utils.PermissionConstants;
import com.ihrm.domain.system.*;
import com.ihrm.system.dao.*;
import com.ihrm.system.dao.PermissionDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
@Transactional
public class PermissionService {
    @Autowired
    private PermissionDao permissionDao;
    @Autowired
    private PermissionMenuDao permissionMenuDao;
    @Autowired
    private PermissionPointDao permissionPointDao;
    @Autowired
    private PermissionApiDao permissionApiDao;
    @Autowired
    private IdWorker idWorker;
    /**
     * 1.保存权限
     */
    public void save(Map<String,Object> map) throws Exception {
        //设置主键的值
        String id = idWorker.nextId()+"";
        //1.通过map构造permission对象
        Permission perm = BeanMapUtils.mapToBean(map,Permission.class);
        perm.setId(id);
        //2.根据类型构造不同的资源对象(菜单,按钮,api)
        int type = perm.getType();
        switch (type) {
            case PermissionConstants.PERMISSION_MENU:
                PermissionMenu menu = BeanMapUtils.mapToBean(map,PermissionMenu.class);
                menu.setId(id);
                permissionMenuDao.save(menu);
                break;
            case PermissionConstants.PERMISSION_POINT:
                PermissionPoint point = BeanMapUtils.mapToBean(map,PermissionPoint.class);
                point.setId(id);
                permissionPointDao.save(point);
                break;
            case PermissionConstants.PERMISSION_API:
                PermissionApi api = BeanMapUtils.mapToBean(map,PermissionApi.class);
                api.setId(id);
                permissionApiDao.save(api);
                break;
            default:
                throw new CommonException(ResultCode.FAIL);
        }
        //3.保存
        permissionDao.save(perm);
    }
    /**
     * 2.更新权限
     */
    public void update(Map<String,Object> map) throws Exception {
        Permission perm = BeanMapUtils.mapToBean(map,Permission.class);
        //1.通过传递的权限id查询权限
        Permission permission = permissionDao.findById(perm.getId()).get();
        permission.setName(perm.getName());
        permission.setCode(perm.getCode());
        permission.setDescription(perm.getDescription());
        permission.setEnVisible(perm.getEnVisible());
        //2.根据类型构造不同的资源
        int type = perm.getType();
        switch (type) {
            case PermissionConstants.PERMISSION_MENU:
                PermissionMenu menu = BeanMapUtils.mapToBean(map,PermissionMenu.class);
                menu.setId(perm.getId());
                permissionMenuDao.save(menu);
                break;
            case PermissionConstants.PERMISSION_POINT:
                PermissionPoint point = BeanMapUtils.mapToBean(map,PermissionPoint.class);
                point.setId(perm.getId());
                permissionPointDao.save(point);
                break;
            case PermissionConstants.PERMISSION_API:
                PermissionApi api = BeanMapUtils.mapToBean(map,PermissionApi.class);
                api.setId(perm.getId());
                permissionApiDao.save(api);
                break;
            default:
                throw new CommonException(ResultCode.FAIL);
        }
        //3.保存
        permissionDao.save(permission);
    }
    /**
     * 3.根据id查询
     *      //1.查询权限
     *      //2.根据权限的类型查询资源
     *      //3.构造map集合
     */
    public Map<String, Object> findById(String id) throws Exception {
        Permission perm = permissionDao.findById(id).get();
        int type = perm.getType();
        Object object = null;
        if(type == PermissionConstants.PERMISSION_MENU) {
            object = permissionMenuDao.findById(id).get();
        }else if (type == PermissionConstants.PERMISSION_POINT) {
            object = permissionPointDao.findById(id).get();
        }else if (type == PermissionConstants.PERMISSION_API) {
            object = permissionApiDao.findById(id).get();
        }else {
            throw new CommonException(ResultCode.FAIL);
        }
        Map<String, Object> map = BeanMapUtils.beanToMap(object);
        map.put("name",perm.getName());
        map.put("type",perm.getType());
        map.put("code",perm.getCode());
        map.put("description",perm.getDescription());
        map.put("pid",perm.getPid());
        map.put("enVisible",perm.getEnVisible());
        return map;
    }
    /**
     * 4.查询全部
     * type      : 查询全部权限列表type:0:菜单 + 按钮(权限点) 1:菜单2:按钮(权限点)3:API接口
     * enVisible : 0:查询所有saas平台的最高权限,1:查询企业的权限
     * pid :父id
     */
    public List<Permission> findAll(Map<String, Object> map) {
        //1.需要查询条件
        Specification<Permission> spec = new Specification<Permission>() {
            /**
             * 动态拼接查询条件
             * @return
             */
            public Predicate toPredicate(Root<Permission> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> list = new ArrayList<>();
                //根据父id查询
                if(!StringUtils.isEmpty(map.get("pid"))) {
                    list.add(criteriaBuilder.equal(root.get("pid").as(String.class),(String)map.get("pid")));
                }
                //根据enVisible查询
                if(!StringUtils.isEmpty(map.get("enVisible"))) {
                    list.add(criteriaBuilder.equal(root.get("enVisible").as(String.class),(String)map.get("enVisible")));
                }
                //根据类型 type
                if(!StringUtils.isEmpty(map.get("type"))) {
                    String ty = (String) map.get("type");
                    CriteriaBuilder.In<Object> in = criteriaBuilder.in(root.get("type"));
                    if("0".equals(ty)) {
                        in.value(1).value(2);
                    }else{
                        in.value(Integer.parseInt(ty));
                    }
                    list.add(in);
                }
                return criteriaBuilder.and(list.toArray(new Predicate[list.size()]));
            }
        };
        return permissionDao.findAll(spec);
    }
    /**
     * 5.根据id删除
     *  //1.删除权限
     *  //2.删除权限对应的资源
     *
     */
    public void deleteById(String id) throws Exception {
        //1.通过传递的权限id查询权限
        Permission permission = permissionDao.findById(id).get();
        permissionDao.delete(permission);
        //2.根据类型构造不同的资源
        int type = permission.getType();
        switch (type) {
            case PermissionConstants.PERMISSION_MENU:
                permissionMenuDao.deleteById(id);
                break;
            case PermissionConstants.PERMISSION_POINT:
                permissionPointDao.deleteById(id);
                break;
            case PermissionConstants.PERMISSION_API:
                permissionApiDao.deleteById(id);
                break;
            default:
                throw new CommonException(ResultCode.FAIL);
        }
    }
}



骚戴理解:需要注意的是菜单、按钮、api这三个子权限的id要和父权限id保持一致,也就是PermissionApi、PermissionMenu、PermissionPoint这三个类的id和Permission类保持一致!

把下面这段Java代码翻译成SQL更好理解

if(!StringUtils.isEmpty(map.get("type"))) {
    String ty = (String) map.get("type");
    CriteriaBuilder.In<Object> in = criteriaBuilder.in(root.get("type"));
    //查询全部权限列表type:0:菜单 + 按钮(权限点) 1:菜单2:按钮(权限点)3:API接口
    if("0".equals(ty)) {
        in.value(1).value(2);
    }else{
        in.value(Integer.parseInt(ty));
    }
    list.add(in);
}



这段Java代码是用于构建JPA动态查询的条件语句。它的作用是根据map中的"type"键值来判断是否需要加入一个子条件。


代码逻辑如下:


检查map中是否存在"type"键,并且其值不为空;

如果符合条件,则获取"type"的值ty,并且使用CriteriaBuilder构造一个In条件对象in;

如果ty为"0",则添加in条件的取值为1和2;否则将ty转换为int类型并添加到in条件中;

最后将这个in条件添加到条件集合list中。

以下是该Java代码对应的SQL语句:

SELECT*FROM 表名 WHERE type IN (1, 2) -- 当map.get("type")中的值为"0"时
SELECT*FROM 表名 WHERE type = ?    -- 当map.get("type")中的值为除"0"以外的值时

这段Java代码会在生成的SQL语句中自动完成参数的绑定,具体SQL语句中的占位符?由框架来替换成具体的值。

控制器

package com.ihrm.system.controller;
import com.ihrm.common.entity.PageResult;
import com.ihrm.common.entity.Result;
import com.ihrm.common.entity.ResultCode;
import com.ihrm.domain.system.Permission;
import com.ihrm.domain.system.User;
import com.ihrm.system.service.PermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
//1.解决跨域
@CrossOrigin
//2.声明restContoller
@RestController
//3.设置父路径
@RequestMapping(value="/sys")
public class PermissionController {
    @Autowired
    private PermissionService permissionService;
    /**
     * 保存
     */
    @RequestMapping(value = "/permission", method = RequestMethod.POST)
    public Result save(@RequestBody Map<String,Object> map) throws Exception {
        permissionService.save(map);
        return new Result(ResultCode.SUCCESS);
    }
    /**
     * 修改
     */
    @RequestMapping(value = "/permission/{id}", method = RequestMethod.PUT)
    public Result update(@PathVariable(value = "id") String id, @RequestBody Map<String,Object> map) throws Exception {
        //构造id
        map.put("id",id);
        permissionService.update(map);
        return new Result(ResultCode.SUCCESS);
    }
    /**
     * 查询列表
     */
    @RequestMapping(value = "/permission", method = RequestMethod.GET)
    public Result findAll(@RequestParam Map map) {
        List<Permission> list =  permissionService.findAll(map);
        return new Result(ResultCode.SUCCESS,list);
    }
    /**
     * 根据ID查询
     */
    @RequestMapping(value = "/permission/{id}", method = RequestMethod.GET)
    public Result findById(@PathVariable(value = "id") String id) throws Exception {
        Map map = permissionService.findById(id);
        return new Result(ResultCode.SUCCESS,map);
    }
    /**
     * 根据id删除
     */
    @RequestMapping(value = "/permission/{id}", method = RequestMethod.DELETE)
    public Result delete(@PathVariable(value = "id") String id) throws Exception {
        permissionService.deleteById(id);
        return new Result(ResultCode.SUCCESS);
    }
}


骚戴理解:这里我一开始搞错了,我以为下面的这个前端的Permission实体就是对应这后端的Permission对象,结果不是!导致我一直不能理解为什么后端要封装成一个map来接受参数而不是直接用Permission对象接收,这个前端的Permission实体其实是后端的Permission、PermissionApi、PermissionMenu、PermissionPoint的合体集合


148ba1074f16041fe20ea6bc248e3572.png

所以这个前端的Permission实体可以用map来接收,也就是通过这么一个前端的Permission实体实现菜单、组件、API的管理,根据前端传过来的type来判断到底要操作什么,这也就是为什么就只有PermissionController这一个控制器却可以实现菜单、组件、API的增删改查操作


前端实现

引入权限管理模块

将资料module-permissions引入到工程的/src文件夹下,在/src/main.js完成模块注册

import permissions from '@/module-permissions/' // 权限管理
Vue.use(permissions, store)


配置API

在/src/api/base/目录下创建permissions.js

import {createAPI} from '@/utils/request'
const api = "/sys/permission"
export const list = data => createAPI(`${api}`, 'get', data)
export const add = data => createAPI(`${api}`, 'post', data)
export const update = data => createAPI(`${api}/${data.id}`, 'put', data)
export const remove = data => createAPI(`${api}/${data.id}`, 'delete', data)
export const detail = data => createAPI(`${api}/${data.id}`, 'get', data)
export const saveOrUpdate = data => {return data.id?update(data):add(data)}


实现权限页面

<template>
  <div class="dashboard-container">
    <div class="app-container">
      <el-card shadow="never">
        <el-button class="filter-item fr" size="small" style="margin-left: 10px;" @click="handleCreate(null,1);setPid(1,'0')" type="primary" icon="el-icon-edit">添加菜单</el-button>
            <el-table :data="dataList" fit style="width: 100%;" highlight-current-row>
                <el-table-column fixed prop="name" label="菜单名称" width="200px">
                    <template slot-scope="scope">
                        <i :class="scope.row.type==1?'ivu-icon fa fa-folder-open-o fa-fw':'ivu-icon  el-icon-view'" 
                            :style="scope.row.type==1?'margin-left: 0px':'margin-left: 20px'"></i>
                        <span @click="show(scope.$index,scope.row.id)">{{scope.row.name}}</span>
                    </template>
                </el-table-column>
                <el-table-column fixed prop="code" label="权限标识" width="200"></el-table-column>
                <el-table-column fixed prop="description" label="描述" width="200"></el-table-column>        
                <el-table-column fixed="right" label="操作">
                    <template slot-scope="scope">
                        <el-button v-if="scope.row.type==1" @click="handleCreate(null,2);setPid(2,scope.row.id)" type="text" size="small">添加权限点</el-button>
                        <el-button @click="handlerApiList(scope.row.id)" type="text" size="small">查看api权限</el-button>
                        <el-button @click="handleCreate(scope.row.id,scope.row.type);setPid(scope.row.type,scope.row.pid)" type="text" size="small">查看</el-button>
                        <el-button @click="handleDelete(scope.row.id)" type="text" size="small">删除</el-button>
                    </template>
                </el-table-column>
            </el-table>
        </el-card>
      </div>
      <el-dialog title="编辑权限" :visible.sync="dialogFormVisible" style="hight:100px;line-height:1px">
          <el-form :model="formData" label-width="90px" style="margin-top:20px">
            <el-form-item label="权限名称">
              <el-input v-model="formData.name" autocomplete="off" style="width:90%"></el-input>
            </el-form-item>
            <el-form-item label="权限标识">
              <el-input v-model="formData.code" autocomplete="off" style="width:90%"></el-input>
            </el-form-item>
            <el-form-item label="权限描述">
              <el-input v-model="formData.description" autocomplete="off" style="width:90%"></el-input>
            </el-form-item>
            <el-form-item label="企业可见">
              <el-switch
                v-model="formData.enVisible"
                active-value="1"
                inactive-value="0"
                active-text="可见"
                inactive-text="不可见">
              </el-switch>
            </el-form-item>  
            <div v-if="type==1">
              <el-form-item label="菜单顺序">
                <el-input v-model="formData.menuOrder" autocomplete="off" style="width:90%"></el-input>
              </el-form-item> 
              <el-form-item label="菜单icon">
                <el-input v-model="formData.menuIcon" autocomplete="off" style="width:90%"></el-input>
              </el-form-item> 
            </div>
            <div v-else-if="type==2">
              <el-form-item label="按钮样式">
                <el-input v-model="formData.pointClass" autocomplete="off" style="width:90%"></el-input>
              </el-form-item> 
              <el-form-item label="按钮icon">
                <el-input v-model="formData.pointIcon" autocomplete="off" style="width:90%"></el-input>
              </el-form-item>  
              <el-form-item label="按钮状态">
                <el-input v-model="formData.pointStatus" autocomplete="off" style="width:90%"></el-input>
              </el-form-item> 
            </div>
            <div v-else-if="type==3">
              <el-form-item label="api请求地址">
                <el-input v-model="formData.apiUrl" autocomplete="off" style="width:90%"></el-input>
              </el-form-item> 
              <el-form-item label="api请求方式">
                <el-input v-model="formData.apiMethod" autocomplete="off" style="width:90%"></el-input>
              </el-form-item>  
              <el-form-item label="api类型">
                <el-input v-model="formData.apiLevel" autocomplete="off" style="width:90%"></el-input>
              </el-form-item>               
            </div>
          </el-form>
          <div slot="footer" class="dialog-footer">
            <el-button @click="dialogFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="saveOrUpdate">确 定</el-button>
          </div>
      </el-dialog>
      <el-dialog  title="API权限列表" :visible.sync="apiDialogVisible" style="hight:400px;line-height:1px">
            <el-button class="filter-item fr" size="small" style="margin-left: 10px;" @click="handleCreate(null,1);setPid(3,pid)" type="primary" icon="el-icon-edit">添加api权限</el-button>
            <el-table :data="apiList" fit style="width: 100%;" max-height="250" >
                <el-table-column fixed prop="name" label="菜单名称" width="120px"></el-table-column>
                <el-table-column fixed prop="code" label="权限标识" width="200"></el-table-column>
                <el-table-column fixed prop="description" label="描述" width="200"></el-table-column>        
                <el-table-column fixed="right" label="操作" width="200">
                    <template slot-scope="scope">
                        <el-button @click="handleCreate(scope.row.id,scope.row.type);setPid(scope.row.type,scope.row.pid)" type="text" size="small">查看</el-button>
                        <el-button @click="handleDelete(scope.row.id);handlerApiList(pid)" type="text" size="small">删除</el-button>
                    </template>
                </el-table-column>
            </el-table>        
      </el-dialog>
  </div>
</template>
<script>
import {saveOrUpdate,list,detail,remove} from "@/api/base/permissions"
export default {
  name: 'permissions-table-index',
  data() {
    return {
      MenuList: 'menuList',
      type:0,
      pid:"",
      dialogFormVisible:false,
      apiDialogVisible:false,
      formData:{},
      dataList:[],
      apiList:[],
      pointEnable:{}
    }
  },
  methods: {
    setPid(type,pid){
      this.pid = pid;
      this.type = type
    },
    handleCreate(id) {
      if(id && id !=undefined) {
        detail({id}).then(res => {
          this.formData = res.data.data
          this.dialogFormVisible=true
        })
      }else{
        this.formData = {}
        this.dialogFormVisible=true
      }
    },
    saveOrUpdate() {
      this.formData.type = this.type
      this.formData.pid = this.pid
      saveOrUpdate(this.formData).then(res => {
        this.$message({message:res.data.message,type:res.data.success?"success":"error"});
        if(res.data.success){
          this.formData={};
          this.dialogFormVisible=false;
        }
        if(this.type ==3){
          this.handlerApiList(this.pid);
        }else{
          this.getList();
          this.pointEnable = {}
        }
      })
    },
    handleDelete(id) {
      remove({id}).then(res=> {
        this.$message({message:res.data.message,type:res.data.success?"success":"error"});
      })
    },
    getList() {
      list({type:1,pid:0}).then(res=> {
          this.dataList = res.data.data
      })
    },
    show(index,id) {
        if(!this.pointEnable[id] == null || this.pointEnable[id]==undefined){
            list({type:2,pid:id}).then(res=> {
                if(res.data.data.length <=0) {
                  this.$message.error("无子权限")
                }else{
                  for(var i = 0 ; i <res.data.data.length;i++) {
                      this.dataList.splice(index+1,0,res.data.data[i]);
                  }
                  this.pointEnable[id] = res.data.data.length;
                }
            })
        }else{
            this.dataList.splice(index+1,this.pointEnable[id])
            this.pointEnable[id] = null;
        }
    },
    handlerApiList(id) {
      this.pid = id;
      list({type:3,pid:id}).then(res=> {
          this.apiList = res.data.data
          this.apiDialogVisible = true;
      })
    }
  },
  created () {
    this.getList();
  }
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.alert {
  margin: 10px 0px;
}
.pagination {
  margin-top: 10px;
  // text-align: right;
}
</style>
<style>
.el-table th {
  background-color: #fafafa;
}
.el-table th.is-leaf {
  border-bottom: 2px solid #e8e8e8;
}
.el-table__row i{ font-style:normal}
</style>


分配角色

需求分析


由于使用了RBAC模型对权限进行统一管理,所以每个SAAS-HRM平台的用户都应该具有角色的信息。进而通过角 色完成对权限的识别。众所周知,一个用户可以具有很多的角色,一个角色可以被分配给不同的用户。所以用户和角色之间是多对多关系。



f36be36df7acbe887c46cc16dbe4cbf9.png


服务端代码实现

  • 改造用户实体类,添加角色的id集合属性,表明一个用户具有的多个角色id


在com.ihrm.system.domain.User用户实体类中添加与角色的多对多关系并进行JPA的配置

@ManyToMany
@JsonIgnore
@JoinTable(name="pe_user_role",joinColumns=
{@JoinColumn(name="user_id",referencedColumnName="id")},
   inverseJoinColumns={@JoinColumn(name="role_id",referencedColumnName="id")}
)
private Set<Role> roles = new HashSet<Role>();//用户与角色   多对多


在com.ihrm.system.domain.Role角色实体类中配置角色与用户的多对多关系并进行JPA配置

@JsonIgnore
@ManyToMany(mappedBy="roles")
private Set<User> users = new HashSet<User>(0);//角色与用户   多对多


骚戴理解:通过在实体类里这样配置就可以实现多个表关联自动修改,也就是你只需要修改实体类他就会自动去维护修改数据库中的关联表和中间表

  • 在com.ihrm.system.controller.UserController添加分配角色的控制器方法实现
    /**
     * 分配角色
     */
    @RequestMapping(value = "/user/assignRoles", method = RequestMethod.PUT)
    public Result assignRoles(@RequestBody Map<String,Object> map) {
        //1.获取被分配的用户id
        String userId = (String) map.get("id");
        //2.获取到角色的id列表
        List<String> roleIds = (List<String>) map.get("roleIds");
        //3.调用service完成角色分配
        userService.assignRoles(userId,roleIds);
        return new Result(ResultCode.SUCCESS);
   }


  • 业务逻辑层添加分配角色的业务方法
    /**
     * 分配角色
     */
    public void assignRoles(String userId,List<String> roleIds) {
        //1.根据id查询用户
        User user = userDao.findById(userId).get();
        //2.设置用户的角色集合
        Set<Role> roles = new HashSet<>();
        for (String roleId : roleIds) {
            Role role = roleDao.findById(roleId).get();
            roles.add(role);
       }
        //设置用户和角色集合的关系
        user.setRoles(roles);
        //3.更新用户
        userDao.save(user);
   }


前端代码实现

  • \src\module-employees 添加分配角色的组件
<template>
  <div class="add-form">
    <el-dialog title="分配角色" :visible.sync="roleFormVisible" style="height:500px">
      <el-form :model="formBase"  label-position="left" label-width="120px" style='margin-left:120px;'>
          <el-checkbox-group 
            v-model="allRoles">
            <el-checkbox v-for="(item,index) in roles" :label="item.id" :key="index">{{item.name}}</el-checkbox>
          </el-checkbox-group>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="createData">提交</el-button>
        <el-button @click="roleFormVisible=false">取消</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import {findAll} from "@/api/base/role"
import {assignRoles,detail} from "@/api/base/users"
export default {
    data () {
        return {
            roleFormVisible:false,
            formBase:{},
            allRoles:[],
            data:[],
            roles:[],
            id:null
        }
    },
    methods: {
        toAssignPrem(id) {
            detail({id:id}).then(res1 => {
                this.allRoles = res1.data.data.roleIds;
                findAll().then(res => {
                    this.id = id;
                    this.roles = res.data.data
                    this.roleFormVisible=true
                })
            })
        },
        createData() {
            assignRoles({id:this.id,roleIds:this.allRoles}).then(res => {
                console.log(this.allRoles)
                this.$message({message:res.data.message,type:res.data.success?"success":"error"});
                this.roleFormVisible=false
            })
        }
    }
}
</script>


  • \src\module-employees\pages\employees-list.vue 引入组件
1. <!--分配角色组件 -->
2. <component v-bind:is="addRole" ref="addRole"></component>

分配权限


需求分析

完成对角色权限的分配。


74c652f01e832ef8e095cfb6b44de62d.png

服务端代码实现

  • 角色实体类中添加与权限的多对多关系并进行JPA配置
@JsonIgnore //忽略json转化
@ManyToMany
@JoinTable(name="pe_role_permission",
        joinColumns={@JoinColumn(name="role_id",referencedColumnName="id")},
        inverseJoinColumns=
{@JoinColumn(name="permission_id",referencedColumnName="id")})
private Set<Permission> permissions = new HashSet<Permission>(0);//角色与模块 多对多


  • 控制器类( com.ihrm.system.controller.RoleController )添加权限分配
    /**
     * 分配权限
     */
    @RequestMapping(value = "/role/assignPrem", method = RequestMethod.PUT)
    public Result assignPrem(@RequestBody Map<String,Object> map) {
        //1.获取被分配的角色的id
        String roleId = (String) map.get("id");
        //2.获取到权限的id列表
        List<String> permIds = (List<String>) map.get("permIds");
        //3.调用service完成权限分配
        roleService.assignPerms(roleId,permIds);
        return new Result(ResultCode.SUCCESS);
   }



  • 服务层中添加分配权限方法
   /**
     * 分配权限
     */
    public void assignPerms(String roleId,List<String> permIds) {
        //1.获取分配的角色对象
        Role role = roleDao.findById(roleId).get();
        //2.构造角色的权限集合
        Set<Permission> perms = new HashSet<>();
        for (String permId : permIds) {
            Permission permission = permissionDao.findById(permId).get();
            //需要根据父id和类型查询API权限列表
            List<Permission> apiList =
            permissionDao.findByTypeAndPid(PermissionConstants.PERMISSION_API, permission.getId());
            perms.addAll(apiList);//自定赋予API权限
            perms.add(permission);//当前菜单或按钮的权限
       }
        System.out.println(perms.size());
        //3.设置角色和权限的关系
        role.setPermissions(perms);
        //4.更新角色
        roleDao.save(role);
   }



骚戴理解:上面的分配权限的逻辑和分配角色的逻辑基本一样的,区别就在于下面这两行代码,如果这个权限是按钮,那么这个按钮一定是绑定了一个api权限的,所以如果前端赋按钮权限,那么api权限也要一起赋予,这里一开始我觉得按钮权限和api权限是一对一的关系,所以不能理解为什么用List来接收,后面发现有的时候一个按钮会发多个请求,例如“分配角色”按钮就会发两个请求

List<Permission> apiList =  permissionDao.findByTypeAndPid(PermissionConstants.PERMISSION_API, permission.getId());
perms.addAll(apiList);//自定赋予API权限


  • 数据层
package com.ihrm.system.dao;
import com.ihrm.domain.system.Permission;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
/**
 * 权限
 */
public interface PermissionDao extends JpaRepository<Permission, String>, JpaSpecificationExecutor<Permission> {
    List<Permission> findByTypeAndPid(int type,String pid);
}


骚戴理解:这里直接在PermissionDao里面写了findByTypeAndPid方法,但是没有写具体的sql,我当时就很纳闷他是怎么实现的,这其实就是根据这个方法的命名来实现的,findByTypeAndPid他会根据这个方法名解析出Type和Pid字段,然后用and拼接成sql语句,实现的效果就是按这两个值去查询


前端代码实现

  • 在\src\module-settings\components\role-list.vue绑定权限按钮
<el-table-column fixed="right" label="操作" align="center" width="250">
   <template slot-scope="scope">
      <el-button @click="handlerPerm(scope.row)" type="text" size="small">分配权限</elbutton>
      <el-button @click="handleUpdate(scope.row)" type="text" size="small">修改</elbutton>
      <el-button @click="handleDelete(scope.row)" type="text" size="small">删除</elbutton>
   </template>
</el-table-column>



在\\src\api\base\role.js中添加分配权限的API方法

export const assignPrem = data => createAPI(`/sys/role/assignPrem`, 'put', data)


\src\module-settings\components\role-list.vue使用Element-UI构造权限树

 <el-dialog :title="'为【'+formData.name+'】分配权限'" :visible.sync="permFormVisible" style="hight:100px;line-height:1px">
      <el-tree
        :data="treeData"
        default-expand-all 
        show-checkbox
        node-key="id"
        ref="tree"
        :default-checked-keys="checkNodes"
        :props="{label:'name'}">
        </el-tree>
        <div slot="footer" class="dialog-footer">
        <el-button @click="permFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="assignPrem">确 定</el-button>
      </div>
    </el-dialog>


  • 完成添加权限

常见的认证机制

HTTP Basic Auth


HTTP Basic Auth简单点说明就是每次请求API时都提供用户的username和password,简言之,Basic Auth是配合RESTful API 使用的最简单的认证方式,只需提供用户名密码即可,但由于有把用户名密码暴露给第三方客户端的风险,在生产环境下被使用的越来越少。因此,在开发对外开放的RESTful API时,尽量避免采用HTTP Basic Auth


Cookie Auth

Cookie认证机制就是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie 对象;通过客户端带上来Cookie对象来与服务器端的session对象匹配来实现状态管理的。默认的,当我们关闭浏 览器的时候,cookie会被删除。但可以通过修改cookie 的expire time使cookie在一定时间内有效


OAuth


OAuth(开放授权)是一个开放的授权标准,允许用户让第三方应用访问该用户在某一web服务上存储的私密的资 源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。 OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的第三方系统(例如,视频 编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样, OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容


82c1e46a99fc0111c2945380ca1218c5.png


这种基于OAuth的认证机制适用于个人消费者类的互联网产品,如社交类APP等应用,但是不太适合拥有自有认证 权限管理的企业应用。




































目录
相关文章
|
7月前
|
JSON JavaScript 数据格式
jwt-auth插件实现了基于JWT(JSON Web Tokens)进行认证鉴权的功能。
jwt-auth插件实现了基于JWT(JSON Web Tokens)进行认证鉴权的功能。
177 1
|
24天前
|
JSON 安全 Go
Go语言中使用JWT鉴权、Token刷新完整示例,拿去直接用!
本文介绍了如何在 Go 语言中使用 Gin 框架实现 JWT 用户认证和安全保护。JWT(JSON Web Token)是一种轻量、高效的认证与授权解决方案,特别适合微服务架构。文章详细讲解了 JWT 的基本概念、结构以及如何在 Gin 中生成、解析和刷新 JWT。通过示例代码,展示了如何在实际项目中应用 JWT,确保用户身份验证和数据安全。完整代码可在 GitHub 仓库中查看。
96 1
|
3月前
|
JSON 安全 数据安全/隐私保护
从0到1搭建权限管理系统系列三 .net8 JWT创建Token并使用
【9月更文挑战第22天】在.NET 8中,从零开始搭建权限管理系统并使用JWT(JSON Web Tokens)创建Token是关键步骤。JWT是一种开放标准(RFC 7519),用于安全传输信息,由头部、载荷和签名三部分组成。首先需安装`Microsoft.AspNetCore.Authentication.JwtBearer`包,并在`Program.cs`中配置JWT服务。接着,创建一个静态方法`GenerateToken`生成包含用户名和角色的Token。最后,在控制器中使用`[Authorize]`属性验证和解析Token,从而实现身份验证和授权功能。
180 3
|
6月前
|
存储 缓存 数据库
【万字长文】微服务整合Shiro+Jwt,源码分析鉴权实战
介绍如何整合Spring Boot、Shiro和Jwt,以实现一个支持RBAC的无状态认证系统。通过生成JWT token,实现用户无状态登录,并能根据用户角色动态鉴权,而非使用Shiro提供的注解,将角色和权限信息硬编码。此外,文章还探讨了如何对Shiro的异常进行统一捕获和处理。作为应届生,笔者在学习Shiro的过程中进行了一些源码分析,尽管可能存在不足和Bug,但希望能为同样需要实现权限管理的开发者提供参考,并欢迎各位大佬指正完善。
360 65
【万字长文】微服务整合Shiro+Jwt,源码分析鉴权实战
|
4月前
|
JSON 数据安全/隐私保护 数据格式
Nest.js 实战 (八):基于 JWT 的路由身份认证鉴权
这篇文章介绍了身份验证的重要性和多种处理策略,重点放在了JWT(JSON Web Token)认证在Nest.js框架中的应用。文章包含了JWT认证的流程,如何在Nest.js中实现,以及如何创建JWT认证策略。包括了安装依赖,创建处理认证流程的文件,以及如何使用HttpException过滤器来处理未登录访问。
222 0
Nest.js 实战 (八):基于 JWT 的路由身份认证鉴权
|
4月前
|
NoSQL 安全 Java
Java Spring Boot中使用Shiro、JWT和Redis实现用户登录鉴权
Java Spring Boot中使用Shiro、JWT和Redis实现用户登录鉴权
|
6月前
|
存储 JSON API
在django3应用中使用现代的JWT鉴权
【6月更文挑战第8天】本文介绍流行的鉴权方式,JSON Web Tokens (JWT) 是一种验证JSON数据所有者的机制,它是一个编码的、安全的字符串,包含可信任的数据且能加密签名。无状态的令牌认证允许客户端存储令牌并将其在每次请求。
68 8
在django3应用中使用现代的JWT鉴权
|
4月前
|
关系型数据库 API Go
[golang]在Gin框架中使用JWT鉴权
[golang]在Gin框架中使用JWT鉴权
110 0
|
7月前
|
负载均衡 Cloud Native 安全
云原生最佳实践系列 6:MSE 云原生网关使用 JWT 进行认证鉴权
本文档介绍了如何在 MSE(Microservices Engine)云原生网关中集成JWT进行全局认证鉴权。
1029 23

热门文章

最新文章