Shiro Integrated SSM Based on Dynamic URL Privilege Management

This case is based on an extension of the previous demo. So the database table, in Shiro Integrated SSM Based on URL Privilege Management (I) The beginning is consistent. If the suggestion of the last demo operation is re-imported, avoid problems.

This time, it is not achieved through fixed annotations written in the method, but through flexible configuration of permissions.

PageController.java

First, PageController.java was annotated with @RequiresPermissions and @RequiresRoles.

 1 import org.springframework.stereotype.Controller;
 2 import org.springframework.web.bind.annotation.RequestMapping;
 3 import org.springframework.web.bind.annotation.RequestMethod;
 4  
 5 //Controller specially designed for displaying pages
 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

Two additional methods, needInterceptor, list Permission URLs

The code is as follows:

 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

For two methods needInterceptor, listPermission URLs have been added to the implementation
Need Interceptor indicates whether to intercept or not, based on the fact that if a url accessed exists in the privilege system, it must intercept. If it doesn't exist, it's released.
This strategy can also be switched to another one, that is, if the access address does not exist in the permission system, it prompts that there is no interception. There is no difference between right and wrong in these two approaches, depending on how the business wishes to develop a privilege strategy.

ListPermission URLs (User users) are used to obtain a set of permission addresses owned by a 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

Path Matching Filter is the shiro built-in filter Path Matching Filter that inherits it.
The basic ideas are as follows:
1. If you are not logged in, jump to logon
2. Access is allowed if the current access path is not maintained in the privilege system
3. If the current user's permission does not include the current access address, then jump to / unauthorized, otherwise access is allowed.

The code is as follows:

 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         // If there is no login, jump to the login page
28         if (!subject.isAuthenticated()) {
29             WebUtils.issueRedirect(request, response, "/login");
30             return false;
31         }
32  
33         // See if there is any maintenance in this path permission. If there is no maintenance, let it go.(It can also be replaced by a non-release policy.)
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                 // This means that the current user has this permission.
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("Current user has no access path " + requestURI + " Authority");
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

First declare the URLPathMatching Filter filter

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


Use this filter in shiro

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

The filtering rule is all access

/** = url

The code is as follows:

  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 Filter -->        
 16     <bean id="urlPathMatchingFilter" class="com.how2java.filter.URLPathMatchingFilter"/>
 17           
 18     <!-- To configure shiro Filter factory class, id- shiroFilter Want to be with us web.xml The filters in the configuration are consistent -->
 19     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
 20         <!-- Call the permission manager we configured -->
 21         <property name="securityManager" ref="securityManager" />
 22         <!-- Configure our login request address -->
 23         <property name="loginUrl" value="/login" />
 24         <!-- If the resource you request is no longer within your permission scope, jump to/403 Request address -->
 25         <property name="unauthorizedUrl" value="/unauthorized" />
 26         <!-- Sign out -->
 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         <!-- Permission configuration -->
 34         <property name="filterChainDefinitions">
 35             <value>
 36                 <!-- anon Indicates that this address is accessible without any permissions -->
 37                 /login=anon
 38                 /index=anon
 39                 /static/**=anon
 40                 <!-- Only the business functions are managed, and the permission configuration itself does not need to do without permission requirements, so as not to confuse beginners. -->
 41                 /config/**=anon
 42                 /doLogout=logout
 43                 <!--All requests(Remove configurable static resource requests or request addresses as anon Request)They all pass through filters. url -->
 44                 /** = url
 45             </value>
 46         </property>
 47     </bean>
 48     <!-- Exit filter -->
 49     <bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
 50         <property name="redirectUrl" value="/index" />
 51     </bean>
 52   
 53     <!-- Conversation ID generator -->
 54     <bean id="sessionIdGenerator"
 55         class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" />
 56     <!-- Conversation Cookie Template Close Browser Invalidates Immediately -->
 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     <!-- Conversation DAO -->
 63     <bean id="sessionDAO"
 64         class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
 65         <property name="sessionIdGenerator" ref="sessionIdGenerator" />
 66     </bean>
 67     <!-- Session validation scheduler, which performs validation every 30 minutes, sets session timeouts and saves -->
 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     <!-- Session Manager -->
 74     <bean id="sessionManager"
 75         class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
 76         <!-- Global session timeout (in milliseconds), default 30 minutes -->
 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     <!-- Safety Manager -->
 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     <!-- Equivalent to call 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     <!-- Password Matcher -->
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     <!-- Guaranteed realization Shiro inside lifecycle Functional bean implement -->
111     <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
112 </beans>

jsp has made some changes in the text

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">Sign in</a><br>
19     </c:if>
20     <c:if test="${!empty subject.principal}">
21         <span class="desc">Hello, ${subject.principal},</span>
22         <a href="doLogout">Sign out</a><br>
23     </c:if>
24          
25     <a href="listProduct">View products</a><span class="desc">(Have permission to view products, zhang3 Yes, li4 Yes)</span><br>
26     <a href="deleteProduct">Delete product</a><span  class="desc">(Delete product privileges, zhang3 Yes, li4 Yes)</span><br>
27     <a href="deleteOrder">Delete order</a><span class="desc">(Have permission to delete orders, zhang3 Yes, li4 No,)</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,Can come in<br>It means possession. deleteOrder Jurisdiction
12     <br>
13     <a href="#"OnClick=" javascript: history. back ()"> Return</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,Can come in<br>It means possession. deleteProduct Jurisdiction
12     <br>
13     <a href="#"OnClick=" javascript: history. back ()"> Return</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,Can come in<br>It means possession. listProduct Jurisdiction
12     <br>
13     <a href="#"OnClick=" javascript: history. back ()"> Return</a>
14 </div>

Open the browser test, finish.

Restart Tomcat Test, Business Test Address:

http://127.0.0.1:8080/shirossm_dynamicURL/index

Privilege Configuration Test Address:

http://127.0.0.1:8080/shirossm_dynamicURL/config/listUser

Why does the role not correspond to the URL

Permissions are flexibly configured through url. But roles do not correspond to urls. Why not match the roles?
From the point of view of code development, it is possible to add a url field to the role table. However, from the perspective of permission management itself, when a url corresponds to both permission table data and role table data, it is prone to confusion.
Instead, in this case, url addresses are only associated with privilege tables, which are logically clear, simple and easier to maintain. So we abandoned the role table and url maintenance.

Finally, similarly, we need demo's message to be private!!!

Keywords: Java Shiro Apache JSP

Added by Jim R on Fri, 11 Oct 2019 10:58:49 +0300