spring-shiro.xml:shiro主配置文件html
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd" default-lazy-init="true"> <description>Shiro安全配置</description> <!-- 項目自定義的Realm --> <bean id="userShiroRealm" class="com.ssm.shiro.UserShiroRealm" /> <!--安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!--設置自定義Realm --> <property name="realm" ref="userShiroRealm" /> <!--將緩存管理器,交給安全管理器 --> <property name="cacheManager" ref="shiroEhcacheManager" /> </bean> <!-- Shiro Filter --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 安全管理器 --> <property name="securityManager" ref="securityManager" /> <!-- 默認的登錄訪問url --> <property name="loginUrl" value="/manage/login" /> <!-- 沒有權限跳轉的url --> <property name="unauthorizedUrl" value="/unauth"/> <!-- 自定義filter配置 --> <property name="filters"> <map> <!-- 將自定義 的FormAuthenticationFilter注入shiroFilter中 --> </map> </property> <property name="filterChainDefinitions"> <value> /manage/login = anon<!-- 不須要認證 能夠理解爲匿名用戶或遊客 --> /manage/loginPost = anon<!-- 不須要認證 能夠理解爲匿名用戶或遊客 --> /guest/** = anon /manage/** = authc </value> </property> </bean> <!-- 用戶受權信息Cache, 採用EhCache --> <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:conf/shiro/ehcache-shiro.xml" /> </bean> <!-- 在方法中 注入 securityManager ,進行代理控制 --> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager" /> <property name="arguments" ref="securityManager" /> </bean> <!-- 保證明現了Shiro內部lifecycle函數的bean執行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- AOP式方法級權限檢查 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor" /> <!-- 啓用shrio受權註解攔截方式 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean> </beans>
ehcache-shiro.xml:Shrio缺省提供了基於ehCache來緩存用戶認證信息和受權信息的實現,該xml爲指定ehCache的配置文件前端
<?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>
ShiroUser.java:自定義Authentication對象,使得Subject除了攜帶用戶的登陸名外還能夠攜帶更多信息java
package com.ssm.shiro; import lombok.Data; import lombok.Getter; import java.io.Serializable; /** * @description:自定義Authentication對象,使得Subject除了攜帶用戶的登陸名外還能夠攜帶更多信息 */ @Data public class ShiroUser implements Serializable { private static final long serialVersionUID = -1373760761780840081L; public String userId; public String userName; public ShiroUser( String userId, String userName) { this.userId = userId; this.userName = userName; } }
UserShiroRealm.java:經過UserShiroRealm 繼承Shiro驗證用戶登陸的類爲自定義的AuthorizingRealmweb
package com.ssm.shiro; import com.ssm.model.User; import com.ssm.service.UserService; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.*; /** * @description:shiro權限認證 * 經過UserShiroRealm 繼承Shiro驗證用戶登陸的類爲自定義的AuthorizingRealm */ public class UserShiroRealm extends AuthorizingRealm { private static Logger LOGGER = LoggerFactory.getLogger(UserShiroRealm.class); @Autowired private UserService userService; /** * 統一用戶登陸入口 * Shiro登陸認證(原理:用戶提交 用戶名和密碼 --- shiro 封裝令牌 ---- realm 經過用戶名將密碼查詢返回 ---- * shiro 自動去比較查詢出密碼和用戶輸入密碼是否一致---- 進行登錄控制 ) * * 其中密碼加密方式:md5 32爲加密 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { LOGGER.info("Shiro開始登陸認證"); UsernamePasswordToken token = (UsernamePasswordToken) authcToken; User user = userService.findUserByUserId(token.getUsername()); // 帳號不存在 if (user == null) { return null; } ShiroUser shiroUser = new ShiroUser(user.getId(), user.getUserName()); // 認證緩存信息 return new SimpleAuthenticationInfo(shiroUser, user.getPassword().toCharArray(), getName()); } /** * Shiro權限認證 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { Set<String> urlSet = new HashSet<String>(); urlSet.add("/manage/user"); urlSet.add("/manage/user/user"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermissions(urlSet); return info; } /** * 清除權限的緩存,但該方法執行後,前端頁面的刷新會略有延遲,這是緩存形成的 * * @author jiangCaiJun */ public void updateAuthz(Long roleId){ Set<String> urlSet = new HashSet<String>(); List<Map<String, String>> roleResourceList= new ArrayList<>(); if (roleResourceList.size() >0) { for (Map<String, String> map : roleResourceList) { if(map != null){ urlSet.add(map.get("user")); } } } //注意:此處模擬塞入資源權限對應的標籤,實際項目中,應從對應的資源與權限關係表中獲取 urlSet.add("/manage/user"); urlSet.add("/manage/user/user"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermissions(urlSet); } }
前臺ajax請求,傳遞username和password過來,封裝成token,進行登陸校驗ajax
@RequestMapping(value = "/loginPost", method = RequestMethod.POST) @ResponseBody public Object loginPost(String username, String password) { if (StringUtils.isBlank(username)) { return renderError("用戶名不能爲空"); } if (StringUtils.isBlank(password)) { return renderError("密碼不能爲空"); } Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { subject.login(token); } catch (UnknownAccountException e) { logger.error("帳號不存在:{}", e); return renderError("帳號不存在"); } catch (DisabledAccountException e) { logger.error("帳號未啓用:{}", e); return renderError("帳號未啓用"); } catch (IncorrectCredentialsException e) { logger.error("密碼錯誤:{}", e); return renderError("密碼錯誤"); } catch (ExcessiveAttemptsException e) { logger.error("登陸失敗屢次,帳戶鎖定:{}", e); return renderError("登陸失敗屢次,帳戶鎖定10分鐘"); } catch (RuntimeException e) { logger.error("未知錯誤,請聯繫管理員:{}", e); return renderError("未知錯誤,請聯繫管理員"); } return renderSuccess(); }
登陸頁面以下spring
輸入正確或錯誤的用戶名和密碼,返回分別以下apache
/** * Shiro權限認證 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { Set<String> urlSet = new HashSet<String>(); urlSet.add("/manage/user"); urlSet.add("/manage/user/user"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermissions(urlSet); return info; }
@Controller @RequestMapping("/manage/user") @RequiresPermissions(value = { "/manage/user" }) public class UserController { private static final Logger LOG = Logger.getLogger(UserController.class); @Autowired private UserService userService; /** * 用戶頁 * * @return */ @RequiresPermissions(value = { "/manage/user/user" }) @RequestMapping(value = "/user", method = RequestMethod.GET) public String userManager(Model model) { String id = "1"; User user = userService.getUser(id); model.addAttribute("user", user); LOG.info(user.toString()); return "user/showUser"; } }
利用<shiro:hasPermission name="/manage/user/user"></shiro:hasPermission">來顯示或隱藏<strong> /manage/user/user</strong>,若沒有顯示,則證實還沒有擁有該權限。緩存
<shiro:hasPermission name="/manage/user/user"> <span>此處權限控制</span> <a href="${staticPath }/manage/user/user"> ${staticPath }/manage/user/user </a> </shiro:hasPermission>
注意:要在頁面上添加shiro標籤安全
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
該頁面的信息以下圖:mybatis
參考連接:Apache Shiro 使用手冊(三)Shiro 受權 - kdboy - ITeye技術網站
具體信息以下圖: