16年的項目裏用過一次shiro,可是時間久遠又沒有作好筆記,忘得差很少了,此次又走了一遍,記錄一下以便之後翻看,如下是流水。javascript
1.加入依賴前端
<!--shiro--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.4.3</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.15</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.1</version> </dependency> <!--shiro-->
2.web.xml里加入過濾器java
<filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
3.將shiro相關配置單獨放在applicationContext-shiro.xml中並在spring配置文件中importweb
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.wqqd.findfocus" annotation-config="true"/> <!-- 1. 配置 SecurityManager--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="cacheManager" ref="cacheManager"/> <property name="authenticator" ref="authenticator"></property> <property name="realms"> <list> <ref bean="jdbcRealm"/> </list> </property> <!--<property name="rememberMeManager.cookie.maxAge" value="10"></property>--> </bean> <!--2. 配置 CacheManager(須要加入 ehcache 的 jar 包及配置文件).--> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> </bean> <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator"> <property name="authenticationStrategy"> <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean><!--多realm時的認證策略--> </property> </bean> <!--3. 配置 Realm(直接配置實現了 org.apache.shiro.realm.Realm 接口的 bean)--> <bean id="jdbcRealm" class="com.wqqd.findfocus.shiro.ShiroRealm"> <property name="credentialsMatcher"> <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="SHA1"></property> <property name="hashIterations" value="10"></property> </bean> </property> </bean> <!--<bean id="secondRealm" class="com.atguigu.shiro.realms.SecondRealm">--> <!--<property name="credentialsMatcher">--> <!--<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">--> <!--<property name="hashAlgorithmName" value="SHA1"></property>--> <!--<property name="hashIterations" value="1024"></property>--> <!--</bean>--> <!--</property>--> <!--</bean>--> <!-- 4. 配置 LifecycleBeanPostProcessor. 能夠自定的來調用配置在 Spring IOC 容器中 shiro bean 的生命週期方法. --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- 5. 啓用 IOC 容器中使用 shiro 的註解. 但必須在配置了 LifecycleBeanPostProcessor 以後纔可使用. --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> <!-- 6. 配置 ShiroFilter. 6.1 id 必須和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致. 若不一致, 則會拋出: NoSuchBeanDefinitionException. 由於 Shiro 會來 IOC 容器中查找和 <filter-name> 名字對應的 filter bean. --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/index.do"/> <property name="successUrl" value="/welcome.do"/> <property name="unauthorizedUrl" value="/unauthorized.do"/> <property name="filters"> <map> <entry key="roles"> <!--經過攔截器將roles[]裏角色之間的'且'改成'或'--> <bean class="com.wqqd.findfocus.shiro.CustomRolesAuthorizationFilter" /> </entry> </map> </property> <!--將下面的filterChainDefinitions內容改成動態獲取--> <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property> <!-- 配置哪些頁面須要受保護. 以及訪問這些頁面須要的權限. 1). anon 能夠被匿名訪問 2). authc 必須認證(即登陸)後纔可能訪問的頁面. 3). logout 登出. 4). roles 角色過濾器 --> <!--<property name="filterChainDefinitions">--> <!--<value>--> <!--/logout.do = logout--> <!--/** = anon--> <!--</value>--> <!--</property>--> </bean> <!--經過實例工廠方法的形式注入map--> <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"></bean> <bean id="filterChainDefinitionMapBuilder" class="com.wqqd.findfocus.shiro.factory.FilterChainDefinitionMapBuilder"></bean> </beans>
下面是對應的幾個beanajax
ShiroRealmspring
package com.wqqd.findfocus.shiro; import com.wqqd.findfocus.bean.UserLoginBean; import com.wqqd.findfocus.common.Const; import com.wqqd.findfocus.pojo.UserAdmin; import com.wqqd.findfocus.service.IMenuSysService; import com.wqqd.findfocus.service.IUserAdminService; import org.apache.shiro.authc.*; 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.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet; import java.util.Set; /** * @author kexiang.bao * @create 2018-09-18 15:59 */ public class ShiroRealm extends AuthorizingRealm{ @Autowired IUserAdminService iUserAdminService; @Autowired IMenuSysService iMenuSysService; @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken; String userAdminAcct = upToken.getUsername(); UserAdmin userAdmin = iUserAdminService.getUserAdminByAcct(userAdminAcct); if(userAdmin == null){ throw new UnknownAccountException(); } UserLoginBean userLoginBean = new UserLoginBean(); userLoginBean.setUserAdmin(userAdmin); Object principal = userLoginBean; Object credentials = userAdmin.getUserAdminPwd(); userAdmin.setUserAdminPwd(""); ByteSource credentialsSalt = ByteSource.Util.bytes(userAdmin.getUserAdminAcct()); String realmName = super.getName(); SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,credentials,credentialsSalt,realmName); return info; } //受權會被 shiro 回調的方法 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { //1. 從 PrincipalCollection 中來獲取登陸用戶的信息 Object principal = principals.getPrimaryPrincipal(); UserLoginBean userLoginBean = (UserLoginBean)principal; UserAdmin userAdmin = userLoginBean.getUserAdmin(); //2. 利用登陸的用戶的信息來用戶當前用戶的角色和權限(可能須要查詢數據庫) String roleCode = userAdmin.getRoleCode(); //超級管理員擁有全部權限 Set<String> permissionSet = null; boolean isSuperAdmin = Const.SUPER_ADMIN_ROLE_CODE.equals(roleCode); if(isSuperAdmin){ permissionSet = iMenuSysService.getAllPermissionCodes(); }else{ permissionSet = iMenuSysService.getPermissionCodes(roleCode); } Set<String> roles = new HashSet<String>(); roles.add(roleCode); //3. 建立 SimpleAuthorizationInfo, 並設置其 roles 屬性和 permissions 屬性. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles); info.addStringPermissions(permissionSet); //4. 返回 SimpleAuthorizationInfo 對象. return info; } }
CustomRolesAuthorizationFilter數據庫
package com.wqqd.findfocus.shiro; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authz.AuthorizationFilter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /** * @author kexiang.bao * @create 2018-09-19 20:26 */ public class CustomRolesAuthorizationFilter extends AuthorizationFilter { @Override protected boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object mappedValue) throws Exception { Subject subject = getSubject(req, resp); String[] rolesArray = (String[]) mappedValue; if (rolesArray == null || rolesArray.length == 0) { //沒有角色限制,有權限訪問 return true; } for (int i = 0; i < rolesArray.length; i++) { if (subject.hasRole(rolesArray[i])) { //若當前用戶是rolesArray中的任何一個,則有權限訪問 return true; } } return false; } }
FilterChainDefinitionMapBuilderapache
package com.wqqd.findfocus.shiro.factory; /** * @author kexiang.bao * @create 2018-09-19 11:46 */ import com.wqqd.findfocus.service.IMenuSysService; import org.springframework.beans.factory.annotation.Autowired; import java.util.LinkedHashMap; public class FilterChainDefinitionMapBuilder { @Autowired IMenuSysService iMenuSysService; public LinkedHashMap<String, String> buildFilterChainDefinitionMap(){ LinkedHashMap<String, String> map = new LinkedHashMap<>(); map.put("/index.do", "anon"); map.put("/validateMsgSend.do","anon"); map.put("/login.do","anon"); map.put("/logout.do","logout"); map.put("/static/**","anon"); map.putAll(iMenuSysService.getRoleMenuMap()); map.put("/**", "authc"); return map; } }
至此就基本結束了。可是還有一個小坑。。就是當ajax請求若是超時或者沒有權限的時候,須要先後端稍微處理一下。後端
@RequestMapping(value = "/index.do") public String jsp(HttpServletRequest request,HttpServletResponse response){ if (SecurityUtils.getSubject().isAuthenticated()){ return "redirect:/welcome.do"; } boolean isAjax = isAjaxRequest(request); if(isAjax) { response.setHeader("Session-Status", "timeout"); } return "login"; } private boolean isAjaxRequest(HttpServletRequest request){ Enumeration<String> values = request.getHeaders("X-Requested-With"); while(values.hasMoreElements()) { String value = values.nextElement(); if("XMLHttpRequest".equalsIgnoreCase(value)) { return true; } } return false; } @RequestMapping(value = "/unauthorized.do") @ResponseBody public ServerResponse<String> unauthorized(HttpServletRequest request,HttpServletResponse response){ boolean isAjax = isAjaxRequest(request); if(isAjax) { response.setHeader("Access-Status", "denied"); } return ServerResponse.createByErrorMessage("您無權進行該操做"); }
前端加上api
$(document).ajaxComplete(function(event, xhr, settings) { if (xhr.getResponseHeader('Session-Status') == 'timeout') { alert("您長時間未操做,請從新登陸"); window.location.reload(); } else if(xhr.getResponseHeader('Access-Status') == 'denied'){ alert("您沒有權限進行該操做!請聯繫管理員"); window.location.reload(); }else if(403 == xhr.status) { window.location.reload(); } });
至此就應該結束了,有問題再回來補充。