第1步:引入shiro相關jar包java
ehcache-core-2.5.0.jarweb
shiro-ehcache-1.2.3.jarspring
shiro-core-1.2.3.jar數據庫
shiro-web-1.2.3.jarapache
shiro-spring-1.2.3.jarspring-mvc
第二步:web.xml配置緩存
<!-- shiro的filter --> <!-- shiro過慮器,DelegatingFilterProxy經過代理模式將spring容器中的bean和filter關聯起來 --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <!-- 設置true由servlet容器控制filter的生命週期 --> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> <!-- 設置spring容器filter的bean id,若是不設置則找與filter-name一致的bean--> <init-param> <param-name>targetBeanName</param-name> <param-value>shiroFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
第三步:applicationContext-shiro.xml配置安全
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "> <!-- web.xml中shiro的filter對應的bean --> <!-- Shiro 的Web過濾器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <!-- loginUrl認證提交地址,若是沒有認證將會請求此地址進行認證,請求此地址將由formAuthenticationFilter進行表單認證 --> <property name="loginUrl" value="/login.action" /> <!-- 認證成功統一跳轉到first.action,建議不配置,shiro認證成功自動到上一個請求路徑 --> <property name="successUrl" value="/first.action"/> <!-- 經過unauthorizedUrl指定沒有權限操做時跳轉頁面--> <property name="unauthorizedUrl" value="/refuse.jsp" /> <!-- 自定義filter配置 --> <property name="filters"> <map> <!-- 將自定義 的FormAuthenticationFilter注入shiroFilter中--> <entry key="authc" value-ref="formAuthenticationFilter" /> </map> </property> <!-- 過慮器鏈定義,從上向下順序執行,通常將/**放在最下邊 --> <property name="filterChainDefinitions"> <value> <!-- 對靜態資源設置匿名訪問 --> /images/** = anon /js/** = anon /styles/** = anon <!-- 驗證碼,可匿名訪問 --> /validatecode.jsp = anon <!-- 請求 logout.action地址,shiro去清除session--> /logout.action = logout <!--商品查詢須要商品查詢權限 ,取消url攔截配置,使用註解受權方式 --> <!-- /items/queryItems.action = perms[item:query] /items/editItems.action = perms[item:edit] --> <!-- 配置記住我或認證經過能夠訪問的地址 --> /index.jsp = user /first.action = user /welcome.jsp = user <!-- /** = authc 全部url都必須認證經過才能夠訪問--> /** = authc <!-- /** = anon全部url均可以匿名訪問 --> </value> </property> </bean> <!-- securityManager安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="customRealm" /> <!-- 注入緩存管理器 --> <property name="cacheManager" ref="cacheManager"/> <!-- 注入session管理器 --> <property name="sessionManager" ref="sessionManager" /> <!-- 記住我 --> <property name="rememberMeManager" ref="rememberMeManager"/> </bean> <!-- realm --> <bean id="customRealm" class="自定義類realm類全名"> <!-- 將憑證匹配器設置到realm中,realm按照憑證匹配器的要求進行散列 --> <property name="credentialsMatcher" ref="credentialsMatcher"/> </bean> <!-- 憑證匹配器 --> <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="md5" /> <property name="hashIterations" value="1" /> </bean> <!-- 緩存管理器 --> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/> </bean> <!-- 會話管理器 --> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- session的失效時長,單位毫秒 --> <property name="globalSessionTimeout" value="600000"/> <!-- 刪除失效的session --> <property name="deleteInvalidSessions" value="true"/> </bean> <!-- 自定義form認證過慮器 --> <!-- 基於Form表單的身份驗證過濾器,不配置將也會註冊此過慮器,表單中的用戶帳號、密碼及loginurl將採用默認值,建議配置 --> <bean id="formAuthenticationFilter" class="自定義類全名 "> <!-- 表單中帳號的input名稱 --> <property name="usernameParam" value="username" /> <!-- 表單中密碼的input名稱 --> <property name="passwordParam" value="password" /> <!-- 記住我input的名稱 --> <property name="rememberMeParam" value="rememberMe"/> </bean> <!-- rememberMeManager管理器,寫cookie,取出cookie生成用戶信息 --> <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <property name="cookie" ref="rememberMeCookie" /> </bean> <!-- 記住我cookie --> <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <!-- rememberMe是cookie的名字 --> <constructor-arg value="rememberMe" /> <!-- 記住我cookie生效時間30天 --> <property name="maxAge" value="2592000" /> </bean> </beans>
第4步:在spring mvc配置中啓用shiro註解支持cookie
<!-- 開啓aop,對類代理 --> <aop:config proxy-target-class="true"></aop:config> <!-- 開啓shiro註解支持 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean>
第5步:自定義Realm和FormAuthenticationFiltersession
public class CustomRealm extends AuthorizingRealm { //注入service @Autowired private SysService sysService; // 設置realm的名稱 @Override public void setName(String name) { super.setName("customRealm"); } //realm的認證方法,從數據庫查詢用戶信息 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { // token是用戶輸入的用戶名和密碼 // 第一步從token中取出用戶名 String userCode = (String) token.getPrincipal(); // 第二步:根據用戶輸入的userCode從數據庫查詢 SysUser sysUser = null; try { sysUser = sysService.findSysUserByUserCode(userCode); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } // 若是查詢不到返回null if(sysUser==null){// return null; } // 從數據庫查詢到密碼 String password = sysUser.getPassword(); //鹽 String salt = sysUser.getSalt(); // 若是查詢到返回認證信息AuthenticationInfo //activeUser就是用戶身份信息 ActiveUser activeUser = new ActiveUser(); activeUser.setUserid(sysUser.getId()); activeUser.setUsercode(sysUser.getUsercode()); activeUser.setUsername(sysUser.getUsername()); //.. //根據用戶id取出菜單 List<SysPermission> menus = null; try { //經過service取出菜單 menus = sysService.findMenuListByUserId(sysUser.getId()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } //將用戶菜單 設置到activeUser activeUser.setMenus(menus); //將activeUser設置simpleAuthenticationInfo SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo( activeUser, password,ByteSource.Util.bytes(salt), this.getName()); return simpleAuthenticationInfo; } // 用於受權 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { //從 principals獲取主身份信息 //將getPrimaryPrincipal方法返回值轉爲真實身份類型(在上邊的doGetAuthenticationInfo認證經過填充到SimpleAuthenticationInfo中身份類型), ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal(); //根據身份信息獲取權限信息 //從數據庫獲取到權限數據 List<SysPermission> permissionList = null; try { permissionList = sysService.findPermissionListByUserId(activeUser.getUserid()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } //單獨定一個集合對象 List<String> permissions = new ArrayList<String>(); if(permissionList!=null){ for(SysPermission sysPermission:permissionList){ //將數據庫中的權限標籤 符放入集合 permissions.add(sysPermission.getPercode()); } } //查到權限數據,返回受權信息(要包括 上邊的permissions) SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); //將上邊查詢到受權信息填充到simpleAuthorizationInfo對象中 simpleAuthorizationInfo.addStringPermissions(permissions); return simpleAuthorizationInfo; } //清除緩存 public void clearCached() { PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals(); super.clearCache(principals); } }
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter { //原FormAuthenticationFilter的認證方法 @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { //在這裏進行驗證碼的校驗 //從session獲取正確驗證碼 HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpSession session =httpServletRequest.getSession(); //取出session的驗證碼(正確的驗證碼) String validateCode = (String) session.getAttribute("validateCode"); //取出頁面的驗證碼 //輸入的驗證和session中的驗證進行對比 String randomcode = httpServletRequest.getParameter("randomcode"); if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){ //若是校驗失敗,將驗證碼錯誤失敗信息,經過shiroLoginFailure設置到request中 httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError"); //拒絕訪問,再也不校驗帳號和密碼 return true; } return super.onAccessDenied(request, response); } }
第六步:用戶登陸控制層代碼
@RequestMapping("login") public String login(HttpServletRequest request)throws Exception{ //若是登錄失敗從request中獲取認證異常信息,shiroLoginFailure就是shiro異常類的全限定名 String exceptionClassName = (String) request.getAttribute("shiroLoginFailure"); //根據shiro返回的異常類路徑判斷,拋出指定異常信息 if(exceptionClassName!=null){ if (UnknownAccountException.class.getName().equals(exceptionClassName)) { //最終會拋給異常處理器 throw new CustomException("帳號不存在"); } else if (IncorrectCredentialsException.class.getName().equals( exceptionClassName)) { throw new CustomException("用戶名/密碼錯誤"); } else if("randomCodeError".equals(exceptionClassName)){ throw new CustomException("驗證碼錯誤 "); }else { throw new Exception();//最終在異常處理器生成未知錯誤 } } //此方法不處理登錄成功(認證成功),shiro認證成功會自動跳轉到上一個請求路徑 //登錄失敗還到login頁面 return "login"; }