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:
Privilege Configuration Test Address:
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!!!