基於shiro進階
更改了數據庫表
之前的PageController是通過@RequiresPermissions和@RequiresRoles進行是否有權限/是否有角色的判定調用@RequestMapping路徑
在PermissionService中加入了兩個方法:needInterceptor, listPermissionURLs
needInterceptor表示是否要進行攔截,判斷依據是如果訪問的某個url,在權限系統里存在,就要進行攔截.
如果不存在就放行了. 這一種策略,也可以切換成另一個,即:
訪問的地址如果不存在於權限系統中,就提示沒有攔截.這兩種做法沒有對錯之分,取決於業務上希望如何制定權限策略。
listPermissionURLs(User user)用來獲取某個用戶所擁有的權限地址集合
package com.how2java.service.impl; 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; } }
注意其中的for循環,是否可以進行更優化的寫法
URLPathMatchingFilter, PathMatchingFilter是shiro內置過濾器.URL...繼承了Path...
基本思路:
1. 如果沒登錄就跳轉到登錄
2. 如果當前訪問路徑沒有在權限系統里維護,則允許訪問
3. 當前用戶所擁有的權限如何不包含當前的訪問地址,則跳轉到/unauthorized, 否則就允許訪問
package com.how2java.filter; 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加入shiro使用這個過濾器
這樣一樣就配置全部的url路徑了,而對應的角色沒有和url對應起來.
為什么不把角色也對應起來,從代碼開發角色來說是可以的,無非是role表添加一個url字段,但是從權限管理本身,當一個url即對應權限表的數據,又對應角色表的數據,
反而容易產生混淆.
這種呢,url地址,僅僅和權限表關聯,從邏輯上明晰簡單,更易維護.
requestURI:/
requestURI:/listProduct
requestURI:/deleteProduct
requestURI:/deleteOrder
requestURI:/deleteOrder
requestURI:/deleteOrder
requestURI:/unauthorized
requestURI:/deleteProduct
requestURI:/listProduct
查看日志也會較為輕松