Spring Boot Shiro 權限管理
標簽: springshiro 2016-01-14 23:44 94587人閱讀 收藏 舉報 本來是打算接著寫關于數據庫方面,集成MyBatis的,剛好趕上朋友問到Shiro權限管理,就先總結下發出來了。
使用Shiro之前用在Spring MVC中,是通過XML文件進行配置。
既然現在在寫Spring Boot的帖子,就將Shiro應用到Spring Boot中,我本地已經完成了SpringBoot使用Shiro的實例,將配置方法共享一下。
先簡單介紹一下Shiro,對于沒有用過Shiro的朋友,也算是做個簡介吧。
Shiro是Apache下的一個開源項目,我們稱之為Apache Shiro。它是一個很易用與Java項目的的安全框架,提供了認證、授權、加密、會話管理,與 Spring Security 一樣都是做一個權限的安全框架,但是與Spring Security 相比,在于 Shiro 使用了比較簡單易懂易于使用的授權方式。
Apache Shiro 的三大核心組件
- Subject 當前用戶操作
- SecurityManager 用于管理所有的Subject
- Realms 用于進行權限信息的驗證,也是我們需要自己實現的。
我們需要實現Realms的Authentication 和 Authorization。其中 Authentication 是用來驗證用戶身份,Authorization 是授權訪問控制,用于對用戶進行的操作授權,證明該用戶是否允許進行當前操作,如訪問某個鏈接,某個資源文件等。
Apache Shiro 核心通過 Filter 來實現,就好像SpringMvc 通過DispachServlet 來主控制一樣。
既然是使用 Filter 一般也就能猜到,是通過URL規則來進行過濾和權限校驗,所以我們需要定義一系列關于URL的規則和訪問權限。
另外我們可以通過Shiro 提供的會話管理來獲取Session中的信息。Shiro 也提供了緩存支持,使用 CacheManager 來管理。
官方網站:http://shiro.apache.org/
完整架構圖:
下面我們通過代碼實戰來看下Spring Boot 中應用Shiro:
1、創建數據庫表
------ -------------- ---------
------ ----------
------ -------- ----------
------- --------- 看截圖,上面3張表是我測試別的用的,可以忽略。
下面是,數據庫腳本和測試數據。
;;;
;
;
;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`test` /*!40100 DEFAULT CHARACTER SET utf8 */;USE `test`;
DROP TABLE IF EXISTS `t_permission`;CREATE TABLE `t_permission` (`id` int(11) NOT NULL AUTO_INCREMENT,`permissionname` varchar(32) DEFAULT NULL,`role_id` int(11) DEFAULT NULL,KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;insert into `t_permission`(`id`,`permissionname`,`role_id`) values (1,'add',2),(2,'del',1),(3,'update',2),(4,'query',3),(5,'user:query',1),(6,'user:edit',2);DROP TABLE IF EXISTS `t_role`;CREATE TABLE `t_role` (`id` int(11) NOT NULL AUTO_INCREMENT,`rolename` varchar(32) DEFAULT NULL,KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;insert into `t_role`(`id`,`rolename`) values (1,'admin'),(2,'manager'),(3,'normal');DROP TABLE IF EXISTS `t_user`;CREATE TABLE `t_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(32) DEFAULT NULL,`password` varchar(32) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;insert into `t_user`(`id`,`username`,`password`) values (1,'tom','123456'),(2,'jack','123456'),(3,'rose','123456');DROP TABLE IF EXISTS `t_user_role`;CREATE TABLE `t_user_role` (`user_id` int(11) DEFAULT NULL,`role_id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;insert into `t_user_role`(`user_id`,`role_id`) values (1,1),(1,3),(2,2),(2,3),(3,3);;
;
;
;
2、創建對應實體類
User.java
package org.springboot.sample.entity;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.hibernate.validator.constraints.NotEmpty;
/*** 用戶** @author 單紅宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月13日*/
@Entity
@Table(name =
"t_user")
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@NotEmpty(message =
"用戶名不能為空")
private String username;
@NotEmpty(message =
"密碼不能為空")
private String password;
@ManyToMany(fetch=FetchType.EAGER)
@JoinTable(name =
"t_user_role", joinColumns = {
@JoinColumn(name =
"user_id") }, inverseJoinColumns = {
@JoinColumn(name =
"role_id") })
private List<Role> roleList;
public User() {
super();}
public User(String username, String password) {
super();
this.username = username;
this.password = password;}
@Transientpublic Set<String>
getRolesName() {List<Role> roles = getRoleList();Set<String> set =
new HashSet<String>();
for (Role role : roles) {set.add(role.getRolename());}
return set;}}
Role.java
package org.springboot.sample.entity;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
/*** 角色(管理員,普通用戶等)** @author 單紅宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月13日*/
@Entity
@Table(name =
"t_role")
public class Role {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String rolename;
@OneToMany(mappedBy =
"role", fetch=FetchType.EAGER)
private List<Permission> permissionList;
@ManyToMany@JoinTable(name =
"t_user_role", joinColumns = {
@JoinColumn(name =
"role_id") }, inverseJoinColumns = {
@JoinColumn(name =
"user_id") })
private List<User> userList;
@Transientpublic List<String>
getPermissionsName() {List<String> list =
new ArrayList<String>();List<Permission> perlist = getPermissionList();
for (Permission per : perlist) {list.add(per.getPermissionname());}
return list;}
}
Permission.java
package org.springboot.sample.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
/*** 權限(增刪改查等)** @author 單紅宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月13日*/
@Entity
@Table(name =
"t_permission")
public class Permission {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String permissionname;
@ManyToOne@JoinColumn(name =
"role_id")
private Role role;}
3、Shiro 配置,相當于SpringMVC 中的XML配置
ShiroConfiguration.java
package org.springboot.sample.config;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springboot.sample.dao.IScoreDao;
import org.springboot.sample.security.MyShiroRealm;
import org.springboot.sample.service.StudentService;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
/*** Shiro 配置** @author 單紅宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月13日*/
@Configuration
public class ShiroConfiguration {private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);
@Beanpublic EhCacheManager
getEhCacheManager() { EhCacheManager em =
new EhCacheManager(); em.setCacheManagerConfigFile(
"classpath:ehcache-shiro.xml");
return em; }
@Bean(name =
"myShiroRealm")
public MyShiroRealm
myShiroRealm(EhCacheManager cacheManager) { MyShiroRealm realm =
new MyShiroRealm(); realm.setCacheManager(cacheManager);
return realm;}
/*** 注冊DelegatingFilterProxy(Shiro)* 集成Shiro有2種方法:* 1. 按這個方法自己組裝一個FilterRegistrationBean(這種方法更為靈活,可以自己定義UrlPattern,* 在項目使用中你可能會因為一些很但疼的問題最后采用它, 想使用它你可能需要看官網或者已經很了解Shiro的處理原理了)* 2. 直接使用ShiroFilterFactoryBean(這種方法比較簡單,其內部對ShiroFilter做了組裝工作,無法自己定義UrlPattern,* 默認攔截 /*)** @param dispatcherServlet* @return* @author SHANHY* @create 2016年1月13日*/
@Bean(name =
"lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor
getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();}
@Beanpublic DefaultAdvisorAutoProxyCreator
getDefaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator daap =
new DefaultAdvisorAutoProxyCreator();daap.setProxyTargetClass(
true);
return daap;}
@Bean(name =
"securityManager")
public DefaultWebSecurityManager
getDefaultWebSecurityManager(MyShiroRealm myShiroRealm) {DefaultWebSecurityManager dwsm =
new DefaultWebSecurityManager();dwsm.setRealm(myShiroRealm);
dwsm.setCacheManager(getEhCacheManager());
return dwsm;}
@Beanpublic AuthorizationAttributeSourceAdvisor
getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {AuthorizationAttributeSourceAdvisor aasa =
new AuthorizationAttributeSourceAdvisor();aasa.setSecurityManager(securityManager);
return aasa;}
/*** 加載shiroFilter權限控制規則(從數據庫讀取然后配置)** @author SHANHY* @create 2016年1月14日*/private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean, StudentService stuService, IScoreDao scoreDao){Map<String, String> filterChainDefinitionMap =
new LinkedHashMap<String, String>();filterChainDefinitionMap.put(
"/user",
"authc");logger.info(
"##################從數據庫讀取權限規則,加載到shiroFilter中##################");filterChainDefinitionMap.put(
"/user/edit/**",
"authc,perms[user:edit]");filterChainDefinitionMap.put(
"/login",
"anon");filterChainDefinitionMap.put(
"/**",
"anon");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);}
/*** ShiroFilter<br/>* 注意這里參數中的 StudentService 和 IScoreDao 只是一個例子,因為我們在這里可以用這樣的方式獲取到相關訪問數據庫的對象,* 然后讀取數據庫相關配置,配置到 shiroFilterFactoryBean 的訪問規則中。實際項目中,請使用自己的Service來處理業務邏輯。** @param myShiroRealm* @param stuService* @param scoreDao* @return* @author SHANHY* @create 2016年1月14日*/@Bean(name =
"shiroFilter")
public ShiroFilterFactoryBean
getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager, StudentService stuService, IScoreDao scoreDao) {ShiroFilterFactoryBean shiroFilterFactoryBean =
new MShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);shiroFilterFactoryBean.setLoginUrl(
"/login");shiroFilterFactoryBean.setSuccessUrl(
"/user");shiroFilterFactoryBean.setUnauthorizedUrl(
"/403");loadShiroFilterChain(shiroFilterFactoryBean, stuService, scoreDao);
return shiroFilterFactoryBean;}}
/*** 繼承 ShiroFilterFactoryBean 處理攔截資源文件問題。** @author 單紅宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年3月8日*/
public class MShiroFilterFactoryBean extends ShiroFilterFactoryBean {private Set<String> ignoreExt;
public MShiroFilterFactoryBean() {
super();ignoreExt =
new HashSet<>();ignoreExt.add(
".jpg");ignoreExt.add(
".png");ignoreExt.add(
".gif");ignoreExt.add(
".bmp");ignoreExt.add(
".js");ignoreExt.add(
".css");}
@Overrideprotected AbstractShiroFilter
createInstance()
throws Exception {SecurityManager securityManager = getSecurityManager();
if (securityManager ==
null) {String msg =
"SecurityManager property must be set.";
throw new BeanInitializationException(msg);}
if (!(securityManager
instanceof WebSecurityManager)) {String msg =
"The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);}FilterChainManager manager = createFilterChainManager();PathMatchingFilterChainResolver chainResolver =
new PathMatchingFilterChainResolver();chainResolver.setFilterChainManager(manager);
return new MSpringShiroFilter((WebSecurityManager) securityManager, chainResolver);}
private final class MSpringShiroFilter extends AbstractShiroFilter {protected MSpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
super();
if (webSecurityManager ==
null) {
throw new IllegalArgumentException(
"WebSecurityManager property cannot be null.");}setSecurityManager(webSecurityManager);
if (resolver !=
null) {setFilterChainResolver(resolver);}}
@Overrideprotected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse,FilterChain chain)
throws ServletException, IOException {HttpServletRequest request = (HttpServletRequest)servletRequest;String str = request.getRequestURI().toLowerCase();
boolean flag =
true;
int idx =
0;
if(( idx = str.indexOf(
".")) >
0){str = str.substring(idx);
if(ignoreExt.contains(str.toLowerCase()))flag =
false;}
if(flag){
super.doFilterInternal(servletRequest, servletResponse, chain);}
else{chain.doFilter(servletRequest, servletResponse);}}}
}
其中的 ehcache-shiro.xml 在 src/main/resources 下面,內容為:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" name="shiroCache"><defaultCache
maxElementsInMemory="10000"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="120"overflowToDisk="false"diskPersistent="false"diskExpiryThreadIntervalSeconds="120"/>
</ehcache> 4、繼承 AuthorizingRealm 實現認證和授權2個方法
MyShiroRealm.java
package org.springboot.sample.security;
import java.util.List;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springboot.sample.dao.IUserDao;
import org.springboot.sample.entity.Role;
import org.springboot.sample.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/*** MyShiroRealm** @author 單紅宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月13日*/
public class MyShiroRealm extends AuthorizingRealm{private static final Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);
@Autowiredprivate IUserDao userDao;
/*** 權限認證,為當前登錄的Subject授予角色和權限 * @see 經測試:本例中該方法的調用時機為需授權資源被訪問時 * @see 經測試:并且每次訪問需授權資源時都會執行該方法中的邏輯,這表明本例中默認并未啟用AuthorizationCache * @see 經測試:如果連續訪問同一個URL(比如刷新),該方法不會被重復調用,Shiro有一個時間間隔(也就是cache時間,在ehcache-shiro.xml中配置),超過這個時間間隔再刷新頁面,該方法會被執行*/@Overrideprotected AuthorizationInfo
doGetAuthorizationInfo(PrincipalCollection principalCollection) {logger.info(
"##################執行Shiro權限認證##################");String loginName = (String)
super.getAvailablePrincipal(principalCollection); User user=userDao.findByName(loginName);
if(user!=
null){SimpleAuthorizationInfo info=
new SimpleAuthorizationInfo();info.setRoles(user.getRolesName());List<Role> roleList=user.getRoleList();
for (Role role : roleList) {info.addStringPermissions(role.getPermissionsName());}
return info;}
return null;}
/*** 登錄認證*/@Overrideprotected AuthenticationInfo
doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken;logger.info(
"驗證當前Subject時獲取到token為:" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE)); User user=userDao.findByName(token.getUsername());
if(user!=
null){
return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());}
return null;}
}
注意:其中 userDao.findByName 這個代碼就不貼上了,也沒啥可貼的,根據姓名查詢一個對象而已。
5、編寫測試的 Controller 和測試 jsp 頁面
ShiroController.java
package org
.springboot.sample.controllerimport java
.util.Mapimport javax
.validation.Validimport org
.apache.shiro.SecurityUtils
import org
.apache.shiro.authc.AuthenticationException
import org
.apache.shiro.authc.ExcessiveAttemptsException
import org
.apache.shiro.authc.IncorrectCredentialsException
import org
.apache.shiro.authc.LockedAccountException
import org
.apache.shiro.authc.UnknownAccountException
import org
.apache.shiro.authc.UsernamePasswordToken
import org
.apache.shiro.subject.Subject
import org
.slf4j
.Logger
import org
.slf4j
.LoggerFactory
import org
.springboot.sample.dao.IUserDao
import org
.springboot.sample.entity.User
import org
.springframework.beans.factory.annotation.Autowired
import org
.springframework.stereotype.Controller
import org
.springframework.ui.Model
import org
.springframework.validation.BindingResult
import org
.springframework.web.bind.annotation.PathVariable
import org
.springframework.web.bind.annotation.RequestMapping
import org
.springframework.web.bind.annotation.RequestMethod
import org
.springframework.web.servlet.mvc.support.RedirectAttributes
@Controller
public class ShiroController {private static final Logger logger = LoggerFactory
.getLogger(ShiroController
.class)@Autowiredprivate IUserDao userDao@RequestMapping(value=
"/login",method=RequestMethod
.GET)public String loginForm(Model model){model
.addAttribute(
"user", new User())return
"login"}@RequestMapping(value=
"/login",method=RequestMethod
.POST)public String login(@Valid User user,BindingResult bindingResult,RedirectAttributes redirectAttributes){if(bindingResult
.hasErrors()){return
"login"}String username = user
.getUsername()UsernamePasswordToken token = new UsernamePasswordToken(user
.getUsername(), user
.getPassword())//獲取當前的Subject Subject currentUser = SecurityUtils
.getSubject()try { //在調用了login方法后,SecurityManager會收到AuthenticationToken,并將其發送給已配置的Realm執行必須的認證檢查 //每個Realm都能在必要時對提交的AuthenticationTokens作出反應 //所以這一步在調用login(token)方法時,它會走到MyRealm
.doGetAuthenticationInfo()方法中,具體驗證方式詳見此方法 logger
.info(
"對用戶[" + username +
"]進行登錄驗證..驗證開始")currentUser
.login(token)logger
.info(
"對用戶[" + username +
"]進行登錄驗證..驗證通過")}catch(UnknownAccountException uae){ logger
.info(
"對用戶[" + username +
"]進行登錄驗證..驗證未通過,未知賬戶")redirectAttributes
.addFlashAttribute(
"message",
"未知賬戶")}catch(IncorrectCredentialsException ice){ logger
.info(
"對用戶[" + username +
"]進行登錄驗證..驗證未通過,錯誤的憑證")redirectAttributes
.addFlashAttribute(
"message",
"密碼不正確")}catch(LockedAccountException lae){ logger
.info(
"對用戶[" + username +
"]進行登錄驗證..驗證未通過,賬戶已鎖定")redirectAttributes
.addFlashAttribute(
"message",
"賬戶已鎖定")}catch(ExcessiveAttemptsException eae){ logger
.info(
"對用戶[" + username +
"]進行登錄驗證..驗證未通過,錯誤次數過多")redirectAttributes
.addFlashAttribute(
"message",
"用戶名或密碼錯誤次數過多")}catch(AuthenticationException ae){ //通過處理Shiro的運行時AuthenticationException就可以控制用戶登錄失敗或密碼錯誤時的情景 logger
.info(
"對用戶[" + username +
"]進行登錄驗證..驗證未通過,堆棧軌跡如下")ae
.printStackTrace()redirectAttributes
.addFlashAttribute(
"message",
"用戶名或密碼不正確")} //驗證是否登錄成功 if(currentUser
.isAuthenticated()){ logger
.info(
"用戶[" + username +
"]登錄認證通過(這里可以進行一些認證通過后的一些系統參數初始化操作)")return
"redirect:/user"}else{ token
.clear()return
"redirect:/login"} }@RequestMapping(value=
"/logout",method=RequestMethod
.GET) public String logout(RedirectAttributes redirectAttributes ){ //使用權限管理工具進行用戶的退出,跳出登錄,給出提示信息SecurityUtils
.getSubject()
.logout()redirectAttributes
.addFlashAttribute(
"message",
"您已安全退出")return
"redirect:/login"} @RequestMapping(
"/403")public String unauthorizedRole(){logger
.info(
"------沒有權限-------")return
"403"}@RequestMapping(
"/user")public String getUserList(Map<String, Object> model){model
.put(
"userList", userDao
.getList())return
"user"}@RequestMapping(
"/user/edit/{userid}")public String getUserList(@PathVariable int userid){logger
.info(
"------進入用戶信息修改-------")return
"user_edit"}
}
login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Login
</title>
</head><body><h1>登錄頁面----${message }
</h1><img alt="" src="${pageContext.request.contextPath }/pic.jpg"><form:form action="${pageContext.request.contextPath }/login"commandName="user" method="post">用戶名:
<form:input path="username" /><form:errors path="username" cssClass="error" /><br />密碼:
<form:password path="password" /><form:errors path="password" cssClass="error" /><br /><form:button name="button">提交
</form:button></form:form>
</body>
</html>user.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><title>用戶列表
</title></head><body><h1>${message }
</h1><h1>用戶列表--
<a href="${pageContext.request.contextPath }/logout">退出登錄
</a> </h1><h2>權限列表
</h2><shiro:authenticated>用戶已經登錄顯示此內容
<br/></shiro:authenticated><br/><shiro:hasRole name="manager">manager角色登錄顯示此內容
<br/></shiro:hasRole><shiro:hasRole name="admin">admin角色登錄顯示此內容
<br/></shiro:hasRole><shiro:hasRole name="normal">normal角色登錄顯示此內容
<br/></shiro:hasRole><br/><shiro:hasAnyRoles name="manager,admin">manager or admin 角色用戶登錄顯示此內容
<br/></shiro:hasAnyRoles><br/><shiro:principal/>-顯示當前登錄用戶名
<br/><br/><shiro:hasPermission name="add">add權限用戶顯示此內容
<br/></shiro:hasPermission><shiro:hasPermission name="user:query">user:query權限用戶顯示此內容
<br/></shiro:hasPermission><shiro:lacksPermission name="user:query">不具有user:query權限的用戶顯示此內容
<br/></shiro:lacksPermission><br/>所有用戶列表:
<br/><ul><c:forEach items="${userList }" var="user"><li>用戶名:${user.username }----密碼:${user.password }----
<a href="${pageContext.request.contextPath }/user/edit/${user.id}">修改用戶(測試根據不同用戶可訪問權限不同,本例tom無權限,jack有權限)
</a></li></c:forEach></ul><img alt="" src="${pageContext.request.contextPath }/pic.jpg"><script type="text/javascript" src="${pageContext.request.contextPath }/webjarslocator/jquery/jquery.js"></script></body>
</html> user_edit.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><title>用戶信息 - 修改
</title></head><body><h2>修改用戶信息頁面
</h2><br/><a href="${pageContext.request.contextPath }/user">返回用戶列表
</a></body>
</html> 403.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><title>權限錯誤
</title></head><body><h1>對不起,您沒有權限請求此連接!
</h1><img alt="" src="${pageContext.request.contextPath }/pic.jpg"></body>
</html> 其中的pic.jpg 是測試代碼遺留的,沒有任何用處。關于 Controller 和 JSP 頁面本文不做介紹,關于Spring Boot 使用Controller 和 JSP ,前面已經有文章介紹。
啟動服務后訪問 http://localhost:8080/myspringboot/user 會自動跳到 login 頁面。
登錄成功后,會打開 user 頁面(關于默認登錄頁、成功成功URL、沒有權限URL,在 ShiroConfiguration 中已經配置)。
在 user 頁面上,不同用戶會根據權限不同顯示不同的內容,下面的修改操作也已經有文字說明,更換賬號測試便知。
然后我們在實際項目中:不但要在頁面上控制不同權限隱藏或將某些操作設置為不可用狀態,還要在實際上控制那個操作背后的請求是真的不可以使用的。(例如:頁面上的修改按鈕已經灰化了,而我知道了修改按鈕正常情況下點擊會觸發的請求,此時我直接模擬這個修改請求,應當是沒有權限的才對,這樣才算是真正的控制了權限。)
附:
Filter Chain定義說明
1、一個URL可以配置多個Filter,使用逗號分隔
2、當設置多個過濾器時,全部驗證通過,才視為通過
3、部分過濾器可指定參數,如perms,roles
Shiro內置的FilterChain
Filter Name Class anon org.apache.shiro.web.filter.authc.AnonymousFilter authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter port org.apache.shiro.web.filter.authz.PortFilter rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter ssl org.apache.shiro.web.filter.authz.SslFilter user org.apache.shiro.web.filter.authc.UserFilter 閱讀全文 ? ? 頂 39 踩 1
總結
以上是生活随笔為你收集整理的Spring Boot Shiro 权限管理的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。