欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Shiro集成SSM基于动态URL权限管理(二)

程序员文章站 2022-05-15 10:39:50
这个案例基于上一个demo扩展而来。所以数据库表,在Shiro集成SSM基于URL权限管理(一)开篇的一致。如果上个demo操作的建议重新导入一次,避免出现问题。 而这次都不是通过固定写在方法上的注解实现的,而是通过权限灵活配置实现的。 PageController.java 首先是PageCont ......

这个案例基于上一个demo扩展而来。所以数据库表,在shiro集成ssm基于url权限管理(一)开篇的一致。如果上个demo操作的建议重新导入一次,避免出现问题。

而这次不是通过固定写在方法上的注解实现的,而是通过权限灵活配置实现的

pagecontroller.java

首先是pagecontroller.java 里原本通过注解方式的@requirespermissions和@requiresroles 注释掉了

Shiro集成SSM基于动态URL权限管理(二)

 1 import org.springframework.stereotype.controller;
 2 import org.springframework.web.bind.annotation.requestmapping;
 3 import org.springframework.web.bind.annotation.requestmethod;
 4  
 5 //专门用于显示页面的控制器
 6 @controller
 7 @requestmapping("")
 8 public class pagecontroller {
 9      
10     @requestmapping("index")
11     public string index(){
12         return "index";
13     }
14      
15 //  @requirespermissions("deleteorder")
16     @requestmapping("deleteorder")
17     public string deleteorder(){
18         return "deleteorder";
19     }
20 //  @requiresroles("productmanager")
21     @requestmapping("deleteproduct")
22     public string deleteproduct(){
23         return "deleteproduct";
24     }
25     @requestmapping("listproduct")
26     public string listproduct(){
27         return "listproduct";
28     }
29      
30     @requestmapping(value="/login",method=requestmethod.get) 
31     public string login(){
32         return "login";
33     }
34     @requestmapping("unauthorized")
35     public string noperms(){
36         return "unauthorized";
37     }
38  
39 }

permissionservice.java

增加了两个方法 needinterceptor,listpermissionurls

代码如下:

 1 import java.util.list;
 2 import java.util.set;
 3  
 4 import com.how2java.pojo.permission;
 5 import com.how2java.pojo.role;
 6  
 7 public interface permissionservice {
 8     public set<string> listpermissions(string username);
 9  
10     public list<permission> list();
11  
12     public void add(permission permission);
13  
14     public void delete(long id);
15  
16     public permission get(long id);
17  
18     public void update(permission permission);
19  
20     public list<permission> list(role role);
21  
22     public boolean needinterceptor(string requesturi);
23  
24     public set<string> listpermissionurls(string username);
25  
26 }

permissionserviceimpl.java

为两个方法 needinterceptor,listpermissionurls 增加了实现
needinterceptor 表示是否要进行拦截,判断依据是如果访问的某个url,在权限系统里存在,就要进行拦截。 如果不存在,就放行了。
这一种策略,也可以切换成另一个,即,访问的地址如果不存在于权限系统中,就提示没有拦截。 这两种做法没有对错之分,取决于业务上希望如何制定权限策略。

listpermissionurls(user user) 用来获取某个用户所拥有的权限地址集合

  1 import java.util.arraylist;
  2 import java.util.hashset;
  3 import java.util.list;
  4 import java.util.set;
  5  
  6 import org.springframework.beans.factory.annotation.autowired;
  7 import org.springframework.stereotype.service;
  8  
  9 import com.how2java.mapper.permissionmapper;
 10 import com.how2java.mapper.rolepermissionmapper;
 11 import com.how2java.pojo.permission;
 12 import com.how2java.pojo.permissionexample;
 13 import com.how2java.pojo.role;
 14 import com.how2java.pojo.rolepermission;
 15 import com.how2java.pojo.rolepermissionexample;
 16 import com.how2java.service.permissionservice;
 17 import com.how2java.service.roleservice;
 18 import com.how2java.service.userservice;
 19  
 20 @service
 21 public class permissionserviceimpl implements permissionservice {
 22  
 23     @autowired
 24     permissionmapper permissionmapper;
 25     @autowired
 26     userservice userservice;
 27     @autowired
 28     roleservice roleservice;
 29     @autowired
 30     rolepermissionmapper rolepermissionmapper;
 31  
 32     @override
 33     public set<string> listpermissions(string username) {
 34         set<string> result = new hashset<>();
 35         list<role> roles = roleservice.listroles(username);
 36  
 37         list<rolepermission> rolepermissions = new arraylist<>();
 38  
 39         for (role role : roles) {
 40             rolepermissionexample example = new rolepermissionexample();
 41             example.createcriteria().andridequalto(role.getid());
 42             list<rolepermission> rps = rolepermissionmapper.selectbyexample(example);
 43             rolepermissions.addall(rps);
 44         }
 45  
 46         for (rolepermission rolepermission : rolepermissions) {
 47             permission p = permissionmapper.selectbyprimarykey(rolepermission.getpid());
 48             result.add(p.getname());
 49         }
 50  
 51         return result;
 52     }
 53  
 54     @override
 55     public void add(permission u) {
 56         permissionmapper.insert(u);
 57     }
 58  
 59     @override
 60     public void delete(long id) {
 61         permissionmapper.deletebyprimarykey(id);
 62     }
 63  
 64     @override
 65     public void update(permission u) {
 66         permissionmapper.updatebyprimarykeyselective(u);
 67     }
 68  
 69     @override
 70     public permission get(long id) {
 71         return permissionmapper.selectbyprimarykey(id);
 72     }
 73  
 74     @override
 75     public list<permission> list() {
 76         permissionexample example = new permissionexample();
 77         example.setorderbyclause("id desc");
 78         return permissionmapper.selectbyexample(example);
 79  
 80     }
 81  
 82     @override
 83     public list<permission> list(role role) {
 84         list<permission> result = new arraylist<>();
 85         rolepermissionexample example = new rolepermissionexample();
 86         example.createcriteria().andridequalto(role.getid());
 87         list<rolepermission> rps = rolepermissionmapper.selectbyexample(example);
 88         for (rolepermission rolepermission : rps) {
 89             result.add(permissionmapper.selectbyprimarykey(rolepermission.getpid()));
 90         }
 91  
 92         return result;
 93     }
 94  
 95     @override
 96     public boolean needinterceptor(string requesturi) {
 97         list<permission> ps = list();
 98         for (permission p : ps) {
 99             if (p.geturl().equals(requesturi))
100                 return true;
101         }
102         return false;
103     }
104  
105     @override
106     public set<string> listpermissionurls(string username) {
107         set<string> result = new hashset<>();
108         list<role> roles = roleservice.listroles(username);
109  
110         list<rolepermission> rolepermissions = new arraylist<>();
111  
112         for (role role : roles) {
113             rolepermissionexample example = new rolepermissionexample();
114             example.createcriteria().andridequalto(role.getid());
115             list<rolepermission> rps = rolepermissionmapper.selectbyexample(example);
116             rolepermissions.addall(rps);
117         }
118  
119         for (rolepermission rolepermission : rolepermissions) {
120             permission p = permissionmapper.selectbyprimarykey(rolepermission.getpid());
121             result.add(p.geturl());
122         }
123  
124         return result;
125     }
126  
127 }

urlpathmatchingfilter.java

pathmatchingfilter 是shiro 内置过滤器 pathmatchingfilter 继承了这个它。
基本思路如下:
1. 如果没登录就跳转到登录
2. 如果当前访问路径没有在权限系统里维护,则允许访问
3. 当前用户所拥有的权限如果不包含当前的访问地址,则跳转到/unauthorized,否则就允许访问

代码如下:

 1 import java.util.set;
 2  
 3 import javax.servlet.servletrequest;
 4 import javax.servlet.servletresponse;
 5  
 6 import org.apache.shiro.securityutils;
 7 import org.apache.shiro.authz.unauthorizedexception;
 8 import org.apache.shiro.subject.subject;
 9 import org.apache.shiro.web.filter.pathmatchingfilter;
10 import org.apache.shiro.web.util.webutils;
11 import org.springframework.beans.factory.annotation.autowired;
12  
13 import com.how2java.service.permissionservice;
14  
15 public class urlpathmatchingfilter extends pathmatchingfilter {
16     @autowired
17     permissionservice permissionservice;
18  
19     @override
20     protected boolean onprehandle(servletrequest request, servletresponse response, object mappedvalue)
21             throws exception {
22         string requesturi = getpathwithinapplication(request);
23  
24         system.out.println("requesturi:" + requesturi);
25  
26         subject subject = securityutils.getsubject();
27         // 如果没有登录,就跳转到登录页面
28         if (!subject.isauthenticated()) {
29             webutils.issueredirect(request, response, "/login");
30             return false;
31         }
32  
33         // 看看这个路径权限里有没有维护,如果没有维护,一律放行(也可以改为一律不放行)
34         boolean needinterceptor = permissionservice.needinterceptor(requesturi);
35         if (!needinterceptor) {
36             return true;
37         } else {
38             boolean haspermission = false;
39             string username = subject.getprincipal().tostring();
40             set<string> permissionurls = permissionservice.listpermissionurls(username);
41             for (string url : permissionurls) {
42                 // 这就表示当前用户有这个权限
43                 if (url.equals(requesturi)) {
44                     haspermission = true;
45                     break;
46                 }
47             }
48  
49             if (haspermission)
50                 return true;
51             else {
52                 unauthorizedexception ex = new unauthorizedexception("当前用户没有访问路径 " + requesturi + " 的权限");
53  
54                 subject.getsession().setattribute("ex", ex);
55  
56                 webutils.issueredirect(request, response, "/unauthorized");
57                 return false;
58             }
59  
60         }
61  
62     }
63 }

applicationcontext-shiro.xml

首先声明urlpathmatchingfilter 过滤器

<bean id="urlpathmatchingfilter" class="com.how2java.filter.urlpathmatchingfilter"/>


shiro中使用这个过滤器

<entry key="url" value-ref="urlpathmatchingfilter" />

过滤规则是所有访问

/** = url

代码如下:

  1 <?xml version="1.0" encoding="utf-8"?>
  2 <beans xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
  3     xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
  4     xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
  5     xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
  6     xmlns:aop="http://www.springframework.org/schema/aop"
  7     xsi:schemalocation="http://www.springframework.org/schema/beans
  8     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx
  9     http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context
 10     http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc
 11     http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop
 12     http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/util
 13     http://www.springframework.org/schema/util/spring-util.xsd">
 14       
 15     <!-- url过滤器 -->        
 16     <bean id="urlpathmatchingfilter" class="com.how2java.filter.urlpathmatchingfilter"/>
 17           
 18     <!-- 配置shiro的过滤器工厂类,id- shirofilter要和我们在web.xml中配置的过滤器一致 -->
 19     <bean id="shirofilter" class="org.apache.shiro.spring.web.shirofilterfactorybean">
 20         <!-- 调用我们配置的权限管理器 -->
 21         <property name="securitymanager" ref="securitymanager" />
 22         <!-- 配置我们的登录请求地址 -->
 23         <property name="loginurl" value="/login" />
 24         <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
 25         <property name="unauthorizedurl" value="/unauthorized" />
 26         <!-- 退出 -->
 27         <property name="filters">
 28             <util:map>
 29                 <entry key="logout" value-ref="logoutfilter" />
 30                 <entry key="url" value-ref="urlpathmatchingfilter" />
 31             </util:map>
 32         </property>
 33         <!-- 权限配置 -->
 34         <property name="filterchaindefinitions">
 35             <value>
 36                 <!-- anon表示此地址不需要任何权限即可访问 -->
 37                 /login=anon
 38                 /index=anon
 39                 /static/**=anon
 40                 <!-- 只对业务功能进行权限管理,权限配置本身不需要没有做权限要求,这样做是为了不让初学者混淆 -->
 41                 /config/**=anon
 42                 /dologout=logout
 43                 <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过过滤器url -->
 44                 /** = url
 45             </value>
 46         </property>
 47     </bean>
 48     <!-- 退出过滤器 -->
 49     <bean id="logoutfilter" class="org.apache.shiro.web.filter.authc.logoutfilter">
 50         <property name="redirecturl" value="/index" />
 51     </bean>
 52   
 53     <!-- 会话id生成器 -->
 54     <bean id="sessionidgenerator"
 55         class="org.apache.shiro.session.mgt.eis.javauuidsessionidgenerator" />
 56     <!-- 会话cookie模板 关闭浏览器立即失效 -->
 57     <bean id="sessionidcookie" class="org.apache.shiro.web.servlet.simplecookie">
 58         <constructor-arg value="sid" />
 59         <property name="httponly" value="true" />
 60         <property name="maxage" value="-1" />
 61     </bean>
 62     <!-- 会话dao -->
 63     <bean id="sessiondao"
 64         class="org.apache.shiro.session.mgt.eis.enterprisecachesessiondao">
 65         <property name="sessionidgenerator" ref="sessionidgenerator" />
 66     </bean>
 67     <!-- 会话验证调度器,每30分钟执行一次验证 ,设定会话超时及保存 -->
 68     <bean name="sessionvalidationscheduler"
 69         class="org.apache.shiro.session.mgt.executorservicesessionvalidationscheduler">
 70         <property name="interval" value="1800000" />
 71         <property name="sessionmanager" ref="sessionmanager" />
 72     </bean>
 73     <!-- 会话管理器 -->
 74     <bean id="sessionmanager"
 75         class="org.apache.shiro.web.session.mgt.defaultwebsessionmanager">
 76         <!-- 全局会话超时时间(单位毫秒),默认30分钟 -->
 77         <property name="globalsessiontimeout" value="1800000" />
 78         <property name="deleteinvalidsessions" value="true" />
 79         <property name="sessionvalidationschedulerenabled" value="true" />
 80         <property name="sessionvalidationscheduler" ref="sessionvalidationscheduler" />
 81         <property name="sessiondao" ref="sessiondao" />
 82         <property name="sessionidcookieenabled" value="true" />
 83         <property name="sessionidcookie" ref="sessionidcookie" />
 84     </bean>
 85   
 86     <!-- 安全管理器 -->
 87     <bean id="securitymanager" class="org.apache.shiro.web.mgt.defaultwebsecuritymanager">
 88         <property name="realm" ref="databaserealm" />
 89         <property name="sessionmanager" ref="sessionmanager" />
 90     </bean>
 91     <!-- 相当于调用securityutils.setsecuritymanager(securitymanager) -->
 92     <bean
 93         class="org.springframework.beans.factory.config.methodinvokingfactorybean">
 94         <property name="staticmethod"
 95             value="org.apache.shiro.securityutils.setsecuritymanager" />
 96         <property name="arguments" ref="securitymanager" />
 97     </bean>
 98   
 99     <!-- 密码匹配器 -->
100     <bean id="credentialsmatcher" class="org.apache.shiro.authc.credential.hashedcredentialsmatcher">
101         <property name="hashalgorithmname" value="md5"/>
102         <property name="hashiterations" value="2"/>
103         <property name="storedcredentialshexencoded" value="true"/>
104     </bean>
105   
106     <bean id="databaserealm" class="com.how2java.realm.databaserealm">
107         <property name="credentialsmatcher" ref="credentialsmatcher"/>
108     </bean>
109       
110     <!-- 保证实现了shiro内部lifecycle函数的bean执行 -->
111     <bean id="lifecyclebeanpostprocessor" class="org.apache.shiro.spring.lifecyclebeanpostprocessor" />
112 </beans>

jsp做了些文字上的改动

index.jsp

 1 <%@ page language="java" contenttype="text/html; charset=utf-8"
 2     pageencoding="utf-8"%>
 3 <html>
 4 <head>
 5 <meta http-equiv="content-type" content="text/html; charset=utf-8">
 6  
 7 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
 8  
 9 <link rel="stylesheet" type="text/css" href="static/css/style.css" />
10  
11 </head>
12 <body>
13  
14 <div class="workingroom">
15     <div class="logindiv">
16      
17     <c:if test="${empty subject.principal}">
18         <a href="login">登录</a><br>
19     </c:if>
20     <c:if test="${!empty subject.principal}">
21         <span class="desc">你好,${subject.principal},</span>
22         <a href="dologout">退出</a><br>
23     </c:if>
24          
25     <a href="listproduct">查看产品</a><span class="desc">(要有查看产品权限, zhang3有,li4 有)</span><br>
26     <a href="deleteproduct">删除产品</a><span  class="desc">(要有删除产品权限, zhang3有,li4 有)</span><br>
27     <a href="deleteorder">删除订单</a><span class="desc">(要有删除订单权限, zhang3有,li4没有)</span><br>
28 </div>
29  
30 </body>
31 </html>

deleteorder.jsp

 1 <%@ page language="java" contenttype="text/html; charset=utf-8"
 2     pageencoding="utf-8" import="java.util.*"%>
 3   
 4 <!doctype html>
 5   
 6 <meta http-equiv="content-type" content="text/html; charset=utf-8">
 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" />
 8  
 9 <div class="workingroom">
10  
11     deleteorder.jsp,能进来<br>就表示拥有 deleteorder 权限
12     <br>
13     <a href="#" onclick="javascript:history.back()">返回</a>
14 </div>

deleteproduct.jsp

 1 <%@ page language="java" contenttype="text/html; charset=utf-8"
 2     pageencoding="utf-8" import="java.util.*"%>
 3   
 4 <!doctype html>
 5   
 6 <meta http-equiv="content-type" content="text/html; charset=utf-8">
 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" />
 8  
 9 <div class="workingroom">
10  
11     deleteproduct.jsp,能进来<br>就表示拥有 deleteproduct 权限
12     <br>
13     <a href="#" onclick="javascript:history.back()">返回</a>
14 </div>

listproduct.jsp

 1 <%@ page language="java" contenttype="text/html; charset=utf-8"
 2     pageencoding="utf-8" import="java.util.*"%>
 3   
 4 <!doctype html>
 5   
 6 <meta http-equiv="content-type" content="text/html; charset=utf-8">
 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" />
 8  
 9 <div class="workingroom">
10  
11     listproduct.jsp,能进来<br>就表示拥有 listproduct 权限
12     <br>
13     <a href="#" onclick="javascript:history.back()">返回</a>
14 </div>

打开浏览器测试,finish

重启tomcat测试,业务测试地址:

http://127.0.0.1:8080/shirossm_dynamicurl/index

权限配置测试地址: 

http://127.0.0.1:8080/shirossm_dynamicurl/config/listuser

为什么角色不对应url

权限通过url进行灵活配置了。 但是角色还没有和url对应起来。 为什么不把角色也对应起来呢?
从代码开发的角度讲是可以做的,无非就是在 role表上增加一个url 字段。 但是从权限管理本身的角度看,当一个url 既对应权限表的数据,又对应角色表的数据,反而容易产生混淆。
反倒是现在这种,url地址,仅仅和权限表关联起来,在逻辑上明晰简单,也更容易维护。 所以就放弃了角色表也进行url维护的做法了。

 最后,同样地,需要demo的留言私发!!!