Shiro集成SSM基于动态URL权限管理(二)
这个案例基于上一个demo扩展而来。所以数据库表,在Shiro集成SSM基于URL权限管理(一)开篇的一致。如果上个demo操作的建议重新导入一次,避免出现问题。
而这次都不是通过固定写在方法上的注解实现的,而是通过权限灵活配置实现的。
PageController.java
首先是PageController.java 里原本通过注解方式的@RequiresPermissions和@RequiresRoles 注释掉了。
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- //专门用于显示页面的控制器
- @Controller
- @RequestMapping("")
- public class PageController {
- @RequestMapping("index")
- public String index(){
- return "index";
- }
- // @RequiresPermissions("deleteOrder")
- @RequestMapping("deleteOrder")
- public String deleteOrder(){
- return "deleteOrder";
- }
- // @RequiresRoles("productManager")
- @RequestMapping("deleteProduct")
- public String deleteProduct(){
- return "deleteProduct";
- }
- @RequestMapping("listProduct")
- public String listProduct(){
- return "listProduct";
- }
- @RequestMapping(value="/login",method=RequestMethod.GET)
- public String login(){
- return "login";
- }
- @RequestMapping("unauthorized")
- public String noPerms(){
- return "unauthorized";
- }
- }
PermissionService.java
增加了两个方法 needInterceptor,listPermissionURLs
代码如下:
- import java.util.List;
- import java.util.Set;
- import com.how2java.pojo.Permission;
- import com.how2java.pojo.Role;
- public interface PermissionService {
- public Set<String> listPermissions(String userName);
- public List<Permission> list();
- public void add(Permission permission);
- public void delete(Long id);
- public Permission get(Long id);
- public void update(Permission permission);
- public List<Permission> list(Role role);
- public boolean needInterceptor(String requestURI);
- public Set<String> listPermissionURLs(String userName);
- }
PermissionServiceImpl.java
为两个方法 needInterceptor,listPermissionURLs 增加了实现
needInterceptor 表示是否要进行拦截,判断依据是如果访问的某个url,在权限系统里存在,就要进行拦截。 如果不存在,就放行了。
这一种策略,也可以切换成另一个,即,访问的地址如果不存在于权限系统中,就提示没有拦截。 这两种做法没有对错之分,取决于业务上希望如何制定权限策略。
listPermissionURLs(User user) 用来获取某个用户所拥有的权限地址集合
- import java.util.ArrayList;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Set;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import com.how2java.mapper.PermissionMapper;
- import com.how2java.mapper.RolePermissionMapper;
- import com.how2java.pojo.Permission;
- import com.how2java.pojo.PermissionExample;
- import com.how2java.pojo.Role;
- import com.how2java.pojo.RolePermission;
- import com.how2java.pojo.RolePermissionExample;
- import com.how2java.service.PermissionService;
- import com.how2java.service.RoleService;
- import com.how2java.service.UserService;
- @Service
- public class PermissionServiceImpl implements PermissionService {
- @Autowired
- PermissionMapper permissionMapper;
- @Autowired
- UserService userService;
- @Autowired
- RoleService roleService;
- @Autowired
- RolePermissionMapper rolePermissionMapper;
- @Override
- public Set<String> listPermissions(String userName) {
- Set<String> result = new HashSet<>();
- List<Role> roles = roleService.listRoles(userName);
- List<RolePermission> rolePermissions = new ArrayList<>();
- for (Role role : roles) {
- RolePermissionExample example = new RolePermissionExample();
- example.createCriteria().andRidEqualTo(role.getId());
- List<RolePermission> rps = rolePermissionMapper.selectByExample(example);
- rolePermissions.addAll(rps);
- }
- for (RolePermission rolePermission : rolePermissions) {
- Permission p = permissionMapper.selectByPrimaryKey(rolePermission.getPid());
- result.add(p.getName());
- }
- return result;
- }
- @Override
- public void add(Permission u) {
- permissionMapper.insert(u);
- }
- @Override
- public void delete(Long id) {
- permissionMapper.deleteByPrimaryKey(id);
- }
- @Override
- public void update(Permission u) {
- permissionMapper.updateByPrimaryKeySelective(u);
- }
- @Override
- public Permission get(Long id) {
- return permissionMapper.selectByPrimaryKey(id);
- }
- @Override
- public List<Permission> list() {
- PermissionExample example = new PermissionExample();
- example.setOrderByClause("id desc");
- return permissionMapper.selectByExample(example);
- }
- @Override
- public List<Permission> list(Role role) {
- List<Permission> result = new ArrayList<>();
- RolePermissionExample example = new RolePermissionExample();
- example.createCriteria().andRidEqualTo(role.getId());
- List<RolePermission> rps = rolePermissionMapper.selectByExample(example);
- for (RolePermission rolePermission : rps) {
- result.add(permissionMapper.selectByPrimaryKey(rolePermission.getPid()));
- }
- return result;
- }
- @Override
- public boolean needInterceptor(String requestURI) {
- List<Permission> ps = list();
- for (Permission p : ps) {
- if (p.getUrl().equals(requestURI))
- return true;
- }
- return false;
- }
- @Override
- public Set<String> listPermissionURLs(String userName) {
- Set<String> result = new HashSet<>();
- List<Role> roles = roleService.listRoles(userName);
- List<RolePermission> rolePermissions = new ArrayList<>();
- for (Role role : roles) {
- RolePermissionExample example = new RolePermissionExample();
- example.createCriteria().andRidEqualTo(role.getId());
- List<RolePermission> rps = rolePermissionMapper.selectByExample(example);
- rolePermissions.addAll(rps);
- }
- for (RolePermission rolePermission : rolePermissions) {
- Permission p = permissionMapper.selectByPrimaryKey(rolePermission.getPid());
- result.add(p.getUrl());
- }
- return result;
- }
- }
URLPathMatchingFilter.java
PathMatchingFilter 是shiro 内置过滤器 PathMatchingFilter 继承了这个它。
基本思路如下:
1. 如果没登录就跳转到登录
2. 如果当前访问路径没有在权限系统里维护,则允许访问
3. 当前用户所拥有的权限如果不包含当前的访问地址,则跳转到/unauthorized,否则就允许访问。
代码如下:
- import java.util.Set;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import org.apache.shiro.SecurityUtils;
- import org.apache.shiro.authz.UnauthorizedException;
- import org.apache.shiro.subject.Subject;
- import org.apache.shiro.web.filter.PathMatchingFilter;
- import org.apache.shiro.web.util.WebUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import com.how2java.service.PermissionService;
- public class URLPathMatchingFilter extends PathMatchingFilter {
- @Autowired
- PermissionService permissionService;
- @Override
- protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)
- throws Exception {
- String requestURI = getPathWithinApplication(request);
- System.out.println("requestURI:" + requestURI);
- Subject subject = SecurityUtils.getSubject();
- // 如果没有登录,就跳转到登录页面
- if (!subject.isAuthenticated()) {
- WebUtils.issueRedirect(request, response, "/login");
- return false;
- }
- // 看看这个路径权限里有没有维护,如果没有维护,一律放行(也可以改为一律不放行)
- boolean needInterceptor = permissionService.needInterceptor(requestURI);
- if (!needInterceptor) {
- return true;
- } else {
- boolean hasPermission = false;
- String userName = subject.getPrincipal().toString();
- Set<String> permissionUrls = permissionService.listPermissionURLs(userName);
- for (String url : permissionUrls) {
- // 这就表示当前用户有这个权限
- if (url.equals(requestURI)) {
- hasPermission = true;
- break;
- }
- }
- if (hasPermission)
- return true;
- else {
- UnauthorizedException ex = new UnauthorizedException("当前用户没有访问路径 " + requestURI + " 的权限");
- subject.getSession().setAttribute("ex", ex);
- WebUtils.issueRedirect(request, response, "/unauthorized");
- return false;
- }
- }
- }
- }
applicationContext-shiro.xml
首先声明URLPathMatchingFilter 过滤器
<bean id="urlPathMatchingFilter" class="com.how2java.filter.URLPathMatchingFilter"/>
在shiro中使用这个过滤器
<entry key="url" value-ref="urlPathMatchingFilter" />
过滤规则是所有访问
/** = url
代码如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
- xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/util
- http://www.springframework.org/schema/util/spring-util.xsd">
- <!-- url过滤器 -->
- <bean id="urlPathMatchingFilter" class="com.how2java.filter.URLPathMatchingFilter"/>
- <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
- <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
- <!-- 调用我们配置的权限管理器 -->
- <property name="securityManager" ref="securityManager" />
- <!-- 配置我们的登录请求地址 -->
- <property name="loginUrl" value="/login" />
- <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
- <property name="unauthorizedUrl" value="/unauthorized" />
- <!-- 退出 -->
- <property name="filters">
- <util:map>
- <entry key="logout" value-ref="logoutFilter" />
- <entry key="url" value-ref="urlPathMatchingFilter" />
- </util:map>
- </property>
- <!-- 权限配置 -->
- <property name="filterChainDefinitions">
- <value>
- <!-- anon表示此地址不需要任何权限即可访问 -->
- /login=anon
- /index=anon
- /static/**=anon
- <!-- 只对业务功能进行权限管理,权限配置本身不需要没有做权限要求,这样做是为了不让初学者混淆 -->
- /config/**=anon
- /doLogout=logout
- <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过过滤器url -->
- /** = url
- </value>
- </property>
- </bean>
- <!-- 退出过滤器 -->
- <bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
- <property name="redirectUrl" value="/index" />
- </bean>
- <!-- 会话ID生成器 -->
- <bean id="sessionIdGenerator"
- class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" />
- <!-- 会话Cookie模板 关闭浏览器立即失效 -->
- <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
- <constructor-arg value="sid" />
- <property name="httpOnly" value="true" />
- <property name="maxAge" value="-1" />
- </bean>
- <!-- 会话DAO -->
- <bean id="sessionDAO"
- class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
- <property name="sessionIdGenerator" ref="sessionIdGenerator" />
- </bean>
- <!-- 会话验证调度器,每30分钟执行一次验证 ,设定会话超时及保存 -->
- <bean name="sessionValidationScheduler"
- class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler">
- <property name="interval" value="1800000" />
- <property name="sessionManager" ref="sessionManager" />
- </bean>
- <!-- 会话管理器 -->
- <bean id="sessionManager"
- class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
- <!-- 全局会话超时时间(单位毫秒),默认30分钟 -->
- <property name="globalSessionTimeout" value="1800000" />
- <property name="deleteInvalidSessions" value="true" />
- <property name="sessionValidationSchedulerEnabled" value="true" />
- <property name="sessionValidationScheduler" ref="sessionValidationScheduler" />
- <property name="sessionDAO" ref="sessionDAO" />
- <property name="sessionIdCookieEnabled" value="true" />
- <property name="sessionIdCookie" ref="sessionIdCookie" />
- </bean>
- <!-- 安全管理器 -->
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
- <property name="realm" ref="databaseRealm" />
- <property name="sessionManager" ref="sessionManager" />
- </bean>
- <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
- <bean
- class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
- <property name="staticMethod"
- value="org.apache.shiro.SecurityUtils.setSecurityManager" />
- <property name="arguments" ref="securityManager" />
- </bean>
- <!-- 密码匹配器 -->
- <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
- <property name="hashAlgorithmName" value="md5"/>
- <property name="hashIterations" value="2"/>
- <property name="storedCredentialsHexEncoded" value="true"/>
- </bean>
- <bean id="databaseRealm" class="com.how2java.realm.DatabaseRealm">
- <property name="credentialsMatcher" ref="credentialsMatcher"/>
- </bean>
- <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
- <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
- </beans>
jsp做了些文字上的改动
index.jsp
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
- <link rel="stylesheet" type="text/css" href="static/css/style.css" />
- </head>
- <body>
- <div class="workingroom">
- <div class="loginDiv">
- <c:if test="${empty subject.principal}">
- <a href="login">登录</a><br>
- </c:if>
- <c:if test="${!empty subject.principal}">
- <span class="desc">你好,${subject.principal},</span>
- <a href="doLogout">退出</a><br>
- </c:if>
- <a href="listProduct">查看产品</a><span class="desc">(要有查看产品权限, zhang3有,li4 有)</span><br>
- <a href="deleteProduct">删除产品</a><span class="desc">(要有删除产品权限, zhang3有,li4 有)</span><br>
- <a href="deleteOrder">删除订单</a><span class="desc">(要有删除订单权限, zhang3有,li4没有)</span><br>
- </div>
- </body>
- </html>
deleteOrder.jsp
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8" import="java.util.*"%>
- <!DOCTYPE html>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <link rel="stylesheet" type="text/css" href="static/css/style.css" />
- <div class="workingroom">
- deleteOrder.jsp,能进来<br>就表示拥有 deleteOrder 权限
- <br>
- <a href="#" onClick="javascript:history.back()">返回</a>
- </div>
deleteProduct.jsp
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8" import="java.util.*"%>
- <!DOCTYPE html>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <link rel="stylesheet" type="text/css" href="static/css/style.css" />
- <div class="workingroom">
- deleteProduct.jsp,能进来<br>就表示拥有 deleteProduct 权限
- <br>
- <a href="#" onClick="javascript:history.back()">返回</a>
- </div>
listProduct.jsp
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8" import="java.util.*"%>
- <!DOCTYPE html>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <link rel="stylesheet" type="text/css" href="static/css/style.css" />
- <div class="workingroom">
- listProduct.jsp,能进来<br>就表示拥有 listProduct 权限
- <br>
- <a href="#" onClick="javascript:history.back()">返回</a>
- </div>
打开浏览器测试,finish。
重启Tomcat测试,业务测试地址:
权限配置测试地址:
为什么角色不对应URL
权限通过url进行灵活配置了。 但是角色还没有和url对应起来。 为什么不把角色也对应起来呢?
从代码开发的角度讲是可以做的,无非就是在 role表上增加一个url 字段。 但是从权限管理本身的角度看,当一个url 既对应权限表的数据,又对应角色表的数据,反而容易产生混淆。
反倒是现在这种,url地址,仅仅和权限表关联起来,在逻辑上明晰简单,也更容易维护。 所以就放弃了角色表也进行url维护的做法了。
最后,同样地,需要demo的留言私发!!!
Shiro集成SSM基于动态URL权限管理(二)的更多相关文章
- 基于shiro+jwt的真正rest url权限管理,前后端分离
代码地址如下:http://www.demodashi.com/demo/13277.html bootshiro & usthe bootshiro是基于springboot+shiro+j ...
- 【shiro】(2)---基于RUL的权限管理
基于RUL的权限管理 我想在写shiro权限管理认证前,先来一个基于URL实现的权限管理控制. 一.基于URI的权限业务逻辑 实现思路: 将系统操作的每个url配置在权限表中,将权限对应 ...
- Shiro集成SSM基于URL权限管理(一)
学习了shiro之后,我们就可以说尝试把shiro加入ssm中,并做一套基于URL的权限管理. 其他的准备工作就不多说了,直接动手操作,看到效果再去理解. 表结构 执行如下,数据库名字可以自行修改,不 ...
- 第十九章 动态URL权限控制——《跟我学Shiro》
目录贴:跟我学Shiro目录贴 用过Spring Security的朋友应该比较熟悉对URL进行全局的权限控制,即访问URL时进行权限匹配:如果没有权限直接跳到相应的错误页面.Shiro也支持类似的机 ...
- Shiro学习(19)动态URL权限限制
用过spring Security的朋友应该比较熟悉对URL进行全局的权限控制,即访问URL时进行权限匹配:如果没有权限直接跳到相应的错误页面.Shiro也支持类似的机制,不过需要稍微改造下来满足实际 ...
- Spring Security 动态url权限控制(三)
一.前言 本篇文章将讲述Spring Security 动态分配url权限,未登录权限控制,登录过后根据登录用户角色授予访问url权限 基本环境 spring-boot 2.1.8 mybatis-p ...
- 基于DDDLite的权限管理OpenAuth.net 1.0版正式发布
距离上一篇OpenAuth.net的文章已经有5个多月了,在这段时间里项目得到了很多朋友的认可,开源中国上面的Star数接近300,于是坚定了我做下去的信心.最近稍微清闲点,正式推出1.0版,并在阿里 ...
- 10.spring-boot基于角色的权限管理页面实现
10.spring-boot基于角色的权限管理页面实现
- 转:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法、shiro认证与shiro授权
原文地址:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法.shiro认证与shiro授权 以下是部分内容,具体见原文. shiro介绍 什么是shiro shiro是Apache ...
随机推荐
- C++编译器和连接器原理
本文转载自新浪永远即等待的博客 几个概念: 1.编译:编译器对源文件进行编译,就是把源文件中的文本形式存在的源代码翻译成机器语言形式的目标文件的过程,在这个过程中,编译器会进行一系列的语法检查.如果 ...
- vigil rpm 包制作
vigil 可以方便的进行服务的监控,以下尝试制作一个rpm 包,方便使用 安装依赖 ruby yum -y install ruby rubygems ruby-devel 修改gem 源 可选, ...
- circus && web comsole docker-compose 独立部署
问题的根本原因是web console 的bug(实际上还是python 对于依赖版本出来不明确) circus 进程docker 镜像 dockerfile FROM python:slim-str ...
- PowerDesigner 画流程图
原因: 以前赶时间写了n长一个类,现在又增加新需求了,but以前怎么写的忘了,虽然注释都有,一个一个方法的看很累,准备把它用面向对象改造一下,不知道时间够不,先试一试在说.之前设计数据库用的Power ...
- 特征的非线性变换(Feature Non-linear Transformation)
有时候特征x和目标y不呈线性关系,线性模型y=wx+b不能很好地反映事物的规律或者无法对事物进行有效分类,因此此时我们需要使用非线性模型. (x=([x1,x2,...,xn])T,w=([w1,w2 ...
- 第03组 Alpha冲刺(1/4)
队名:不等式方程组 组长博客 作业博客 团队项目进度 组员一:张逸杰(组长) 过去两天完成的任务: 文字/口头描述: 制定了初步的项目计划,并开始学习一些推荐.搜索类算法 GitHub签入纪录: 暂无 ...
- js无限轮播算法中干掉if判断
无限轮播在网页应用中经常见到,这其中算法各有千秋,在学习算法分析一书中发现自增取余方法可以干掉一些不必要的if判断,具体代码如下: var arr= [1,2,3,4,5,6,7,8]; var in ...
- C语言博客作业--结构体,文件
1.本章学习总结(2分) 1.1 学习内容总结 (1)结构体如何定义.成员如何赋值 结构体的一般形式为: struct 结构体名 { 数据类型 成员名1: 数据 ...
- queue怎么用咧↓↓↓
queue(队列) 定义:queue <int> a; 插入队尾:a.push(x); 查询队尾:a.back(); 查询队首:a.front(); 删除队首:a.pop(); 查询长度: ...
- HttpClient 发送请求和参数
发送请求 没有参数 private static void getData() { String timeStamp = String.valueOf(System.currentTimeMillis ...