一、redis搭建

二、WEB层主要依赖包

三、FeignClient通用接口

以上三项,参考《微服务迁移记(五):WEB层搭建(1)

四、SpringSecurity集成

参考:《微服务迁移记(五):WEB层搭建(2)-SpringSecurity集成

五、FreeMarker集成

参考:《微服务迁移记(五):WEB层搭建(3)-FreeMarker集成

六、简单权限管理

实现一个简单的到按钮级权限管理,基于数据库扩展。不支持数据级权限,菜单只到二级(可以扩展至无限级,一般MIS系统,三级应该够了)

1. 数据库设计

用户表:tb_user

角色表:tb_role

用户角色:tb_user_role,外键:user_id,role_id

菜单表:tb_tree

菜单功能按钮表:tb_tree_func,外键:tree_id

角色菜单权限:tb_role_right(应该叫tb_role_tree更合适点),外键:role_id,tree_id

角色菜单下功能按钮:tb_roletree_func,外键:role_right_id,tree_id,role_tree_func(这个键没有拉外键过来),三个外键组成联合主键。

外键关系见下图

2. 用户登录后,左侧二级菜单展示

数据源:获取所有菜单及按钮的权限,前文提到的SpringSecurity里,MyUserDetail里我加过一个roletree的字段,会一起扔到session里(用户更换权限后,需要退出重新登录才能生效)

//获取所有树及按钮,含当前角色是否有权限
public Map<String,Object> getAllTreeWithRoleRight(int role_id){
String sql = "SELECT a.tree_id,a.tree_name,a.sup_tree_id,a.taxis,IFNULL(b.tree_id,0) as perm FROM " +
" tb_tree a left JOIN " +
"(SELECT rr.tree_id FROM tb_role_right rr " +
"WHERE rr.role_id=?) b " +
"ON b.tree_id=a.tree_id " +
" WHERE a.del_flag=0 AND enable_flag=1 " +
" ORDER BY sup_tree_id ASC,taxis ASC;";
List<Map<String,Object>> tree = this.jdbcTemplate.queryForList(sql,role_id);
//再查询所有按钮
sql = "SELECT a.tree_id,b.tree_func_name,b.tree_func_namedesc,IFNULL(c.tree_id,0) as perm FROM tb_tree a INNER JOIN tb_tree_func b ON b.tree_id=a.tree_id " +
"left JOIN " +
"(SELECT rt.tree_id,rt.role_id,rt.tree_func_name FROM tb_role_treefunc rt WHERE rt.role_id=?) c " +
"ON c.tree_id=a.tree_id AND c.tree_func_name=b.tree_func_name";
List<Map<String,Object>> func = jdbcTemplate.queryForList(sql,role_id);
Map<String,Object> map = new HashMap<String,Object>();
map.put("tree",tree);
map.put("func",func);
return map;
}

freemarker前台展示二级菜单:

<div id="sidebar-collapse" class="col-sm-3 col-lg-2 sidebar">
<ul class="nav menu">
<li class="active"><a href="javascript:goto('0000','/home');"><span class="glyphicon glyphicon-home"></span> 首页</a></li>
<#list treelist as tree>
<#if tree.sup_tree_id == >
<li class="parent ">
<a data-toggle="collapse" href="#sub-item-${tree.tree_id?c}">
<span class="glyphicon ${tree.tree_icon}"></span> ${tree.tree_name}
</a>
<ul class="children collapse" id="sub-item-${tree.tree_id?c}">
<#list treelist as tree_c>
<#if tree_c.sup_tree_id==tree.tree_id>
<li><a href="javascript:goto(${tree_c.tree_id?c},'${tree_c.link_addr!}')"><span class="glyphicon ${tree_c.tree_icon}"></span> ${tree_c.tree_name}</a></li>
</#if>
</#list>
</ul>
</li>
</#if>
</#list>
<li role="presentation" class="divider"></li>
<li><a href="/manage/Login/logout"><span class="glyphicon glyphicon-user"></span> 注销</a></li>
</ul>
</div><!--/.sidebar-->

最终展示效果如下:

2. 模块中按钮权限控制

通过自定义FreeMarker标签,实现前台按钮展示或隐藏

PermissionTagDirective 继承自TemplateDirectiveModel ,引用时需要传递tree_id和permissiontype两个字段进来。

package com.zyproject.web.freemarker;

import com.zyproject.entity.RoleTreefuncEntity;
import com.zyproject.entity.UserEntity;
import com.zyproject.web.secrity.MyUserDetails;
import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component; import java.io.IOException;
import java.util.List;
import java.util.Map; /**
* @program: zyproject
* @description: freemarker自定义标签,实现按钮级权限控制
* @author: zhouyu(zhouyu629 # qq.com)
* @create: 2020-02-18
**/
@Component
public class PermissionTagDirective implements TemplateDirectiveModel {
private static String permissionType = "ptype"; //A新增,D删除等,与数据库对应
private static String Tree = "tree_id"; //菜单id
@Override
public void execute(Environment environment, Map map,
TemplateModel[] templateModels,
TemplateDirectiveBody templateDirectiveBody) throws TemplateException, IOException {
String ptype = map.get(permissionType).toString();
String tree_id = map.get(Tree).toString();
//当前登录用户
MyUserDetails myUserDetails = (MyUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
//判断权限,当前tree下,是否有ptype权限
if(this.checkRight(myUserDetails.getTreefuncEntities(),ptype,tree_id)) {
templateDirectiveBody.render(environment.getOut());
}
} private boolean checkRight(List<RoleTreefuncEntity> roleTreefuncEntities,String ptype,String tree_id){
for (RoleTreefuncEntity roleTreefuncEntity :roleTreefuncEntities) {
if(roleTreefuncEntity.getTree_id() == Integer.valueOf(tree_id) && roleTreefuncEntity.getTree_func_name().equals(ptype)){
return true;
}
}
return false;
}
}

然后把这个标签注册到FreeMarker

package com.zyproject.web.freemarker;

import freemarker.template.Configuration;
import freemarker.template.TemplateException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; import javax.annotation.PostConstruct;
import java.io.IOException; /**
* @program: zyproject
* @description: 注册标签到Freemarker
* @author: zhouyu(zhouyu629 # qq.com)
* @create: 2020-02-18
**/
@Component
public class CustomeFreemakerConfigure implements ApplicationContextAware {
@Autowired
Configuration configuration; @Autowired
private PermissionTagDirective permissionTagDirective; @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { } @PostConstruct
public void setSharedVariable() throws IOException, TemplateException{
configuration.setSharedVariable("perm",permissionTagDirective);
}
}

前台引用标签,标签里的参数tree_id,是在BaseController里扔到前台的,所有Controller都继承自BaseController(这部分代码略)

<@perm tree_id="${tree_id}" ptype="M">
<button name="btnModify" onclick="edit(${roles.role_id})" type="button" class="btn btn-primary btn-sm"><span class="glyphicon glyphicon-th" aria-hidden="true"></span>修改</button>
</@perm>
<@perm tree_id="${tree_id}" ptype="D">
<button name="btnDel" type="button" onclick="del(${roles.role_id})" class="btn btn-danger btn-sm"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span>删除</button>
</@perm>
<@perm tree_id="${tree_id}" ptype="R">
<button name="btnRight" type="button" onclick="setRight(${roles.role_id})" class="btn btn-success btn-sm"><span class="glyphicon glyphicon-tags" aria-hidden="true"></span>权限</button>
</@perm>

最终展示效果如下,后面三个按钮根据角色权限分配得来:

待完善:

1. BaseController里还要判断下进入每个模块是否有相应的权限,防止用户通过直接输入url地址绕过自定义标签的判断

2. 目前权限只到菜单和按钮级别,如果这个功能模块有多个子界面,权限都跟主界面保持一致

微服务迁移记(五):WEB层搭建(4)-简单的权限管理的更多相关文章

  1. 微服务迁移记(五):WEB层搭建(5)-集成ueditor编辑器,伪分布式图片上传

    一.redis搭建 二.WEB层主要依赖包 三.FeignClient通用接口 以上三项,参考<微服务迁移记(五):WEB层搭建(1)> 四.SpringSecurity集成 参考:< ...

  2. 微服务迁移记(五):WEB层搭建(2)-SpringSecurity集成

    一.redis搭建 二.WEB层主要依赖包 三.FeignClient通用接口 以上三项,参考<微服务迁移记(五):WEB层搭建(1)> 接下来,集成SpringSecruity,实现用户 ...

  3. 微服务迁移记(五):WEB层搭建(3)-FreeMarker集成

    一.redis搭建 二.WEB层主要依赖包 三.FeignClient通用接口 以上三项,参考<微服务迁移记(五):WEB层搭建(1)> 四.SpringSecurity集成 参考:< ...

  4. 微服务迁移记(五):WEB层搭建(1)

    WEB层是最终表现层,注册至注册中心,引用接口层(不需要引用实现层).公共服务层.用户登录使用SpringSecurity,Session保存在redis中,权限管理没有用SpringSecurity ...

  5. docker微服务部署之:二、搭建文章微服务项目

    docker微服务部署之:一,搭建Eureka微服务项目 一.新增demo_article模块,并编写代码 右键demo_parent->new->Module->Maven,选择M ...

  6. docker微服务部署之:三,搭建Zuul微服务项目

    docker微服务部署之:二.搭建文章微服务项目 一.新增demo_eureka模块,并编写代码 右键demo_parent->new->Module->Maven,选择Module ...

  7. docker微服务部署之:一,搭建Eureka微服务项目

    先说明一下docker需要搭建的微服务的基本情况: 项目情况:一个demo_parent项目,下面三个子模块:demo_eureka(eureka服务).demo_article(文章服务).demo ...

  8. 微服务实践(五):微服务的事件驱动数据管理 - DockOne.io

    原文:微服务实践(五):微服务的事件驱动数据管理 - DockOne.io [编者的话]本文是使用微服务创建应用系列的第五篇文章.第一篇文章介绍了微服务架构模式,并且讨论了使用微服务的优缺点:第二和第 ...

  9. 微服务(入门四):identityServer的简单使用(客户端授权)

    IdentityServer简介(摘自Identity官网) IdentityServer是将符合规范的OpenID Connect和OAuth 2.0端点添加到任意ASP.NET核心应用程序的中间件 ...

随机推荐

  1. STL初步学习(map)

    3.map map作为一个映射,有两个参数,第一个参数作为关键值,第二个参数为对应的值,关键值是唯一的 在平时使用的数组中,也有点类似于映射的方法,例如a[10]=1,但其实我们的关键值和对应的值只能 ...

  2. Django快速开发实践:Drf框架和xadmin配置指北

    步骤 既然是快速开发,那废话不多说,直接说步骤: 安装Djagno 安装Django Rest Framework 定义models 定义Rest framework的serializers 定义Re ...

  3. HDU 3686 Traffic Real Time Query System (图论)

    HDU 3686 Traffic Real Time Query System 题目大意 给一个N个点M条边的无向图,然后有Q个询问X,Y,问第X边到第Y边必需要经过的点有多少个. solution ...

  4. 「JLOI2015」城池攻占 可并堆

    传送门 分析 如果直接暴力枚举的话肯定会超时 我们可以从下往上遍历,维护一个小根堆 每次到达一个节点把战败的骑士扔出去 剩下的再继续向上合并,注意要维护一下其实的战斗力 可以像线段树那样用一个lazy ...

  5. 重学 Java 设计模式:实战模版模式「模拟爬虫各类电商商品,生成营销推广海报场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 黎明前的坚守,的住吗? 有人举过这样一个例子,先给你张北大的录 ...

  6. Java入门系列之final

    前言 在C#经典面试中掺杂过Java的final关键字,主要用于类不能被继承,在C#则是利用关键字seal修饰类为密封类,而在Java中的final关键字的具体用法包含C#中const.readonl ...

  7. day49 数据库终章

    目录 一.pymysql补充 二.数据库补充 1 视图(了解) 2 触发器(了解) 3 事务 4 存储过程(了解) 5 函数 6 流程控制 7 索引 8 b+树 9 聚集索引(primary key) ...

  8. day17 作业

    目录 一.编写函数(函数执行的时间用time.sleep(n)模拟) 二.编写装饰器,为函数加上统计时间的功能 三.编写装饰器,为函数加上认证的功能 四.编写装饰器,为多个函数加上认证的功能(用户的账 ...

  9. Scala 基础(三):Scala语言快速开发入门

    1.Scala执行流程分析 2.Scala程序开发注意事项(重点) Scala源文件以 “.scala" 为扩展名. Scala程序的执行入口是main()函数. Scala语言严格区分大小 ...

  10. java 面向对象(二十):类的结构:代码块

    类的成员之四:代码块(初始化块)(重要性较属性.方法.构造器差一些)1.代码块的作用:用来初始化类.对象的信息2.分类:代码块要是使用修饰符,只能使用static分类:静态代码块 vs 非静态代码块3 ...