spring mvc 整合shiro 做許可權的簡單使用
1.概述
現在的專案使用的許可權控制系統是spring security 3.因為專案的框架使用spring,就順便使用了。最近研究了一下spring side4,推薦使用shiro。照著示例做了一遍。在原有的spring web工程中。步驟如下。
2.引進包,maven設定
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.2.1</version> <type>jar</type> <scope>compile</scope> </dependency>
3.實現Controller層
主要是登陸url和幾個掩飾url
@Controller public class AdminController { @RequestMapping(value = "/admin/index", method = RequestMethod.GET) public String index(Model model) { return "admin/index"; } @RequestMapping(value = "/admin/login", method = RequestMethod.GET) public String login(Model model) { logger.info("login get"); return "admin/login"; } @RequestMapping(value = "/admin/login", method = RequestMethod.POST) public String doLogin(Model model) { logger.info("login post"); return "admin/login"; } @RequiresRoles("user") @RequestMapping(value = "/admin/user", method = RequestMethod.GET) public String shiroUser(Model model) { return "admin/index"; } @RequiresRoles("admin") @RequestMapping(value = "/admin/admin", method = RequestMethod.GET) public String shiroAdmin(Model model) { return "admin/index"; } Logger logger = LoggerFactory.getLogger(AdminController.class); }
4.實現許可權驗證
繼承shiro的AuthorizingRealm
public class ShiroDbRealm extends AuthorizingRealm { protected AccountService accountService; @Autowired public void setAccountService(AccountService accountService) { this.accountService = accountService; } /** * 授權查詢回撥函式, 進行鑑權但快取中無使用者的授權資訊時呼叫. */ @SuppressWarnings("unused") @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection p) { logger.info("授權認證:" + p.getRealmNames()); ShiroUser shiroUser = (ShiroUser) p.getPrimaryPrincipal(); User user = accountService.findUserByLoginName(shiroUser.loginName); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); for (Role role : user.getRoleList()) { //基於Role的許可權資訊 info.addRole(role.getName()); //基於Permission的許可權資訊 info.addStringPermission(role.getPermissions()); } return info; } /** * 認證回撥函式,登入時呼叫. */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authcToken) throws AuthenticationException { logger.info("authc pass:"); UsernamePasswordToken token = (UsernamePasswordToken) authcToken; logger.info("authc name:" + token.getUsername()); User user = accountService.findUserByLoginName(token.getUsername()); if (user != null) { if (user.getStatus().equals("disabled")) { throw new DisabledAccountException(); } logger.info("authc name:" + token.getUsername() + " user:" + user.getLoginName() + " pwd:" + user.getPassword() + "getname:" + getName()); // byte[] salt = Encodes.decodeHex(user.getSalt()); return new SimpleAuthenticationInfo(new ShiroUser(user.getLoginName(), user.getName()), user.getPassword(), getName()); } return null; } /** * 自定義Authentication物件,使得Subject除了攜帶使用者的登入名外還可以攜帶更多資訊. */ public static class ShiroUser implements Serializable { private static final long serialVersionUID = -1373760761780840081L; public String loginName; public String name; public ShiroUser(String loginName, String name) { this.loginName = loginName; this.name = name; } public String getName() { return loginName; } /** * 本函式輸出將作為預設的<shiro:principal/>輸出. */ @Override public String toString() { return loginName; } /** * 過載hashCode,只計算loginName; */ @Override public int hashCode() { return Objects.hashCode(loginName); } /** * 過載equals,只計算loginName; */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ShiroUser other = (ShiroUser) obj; if (loginName == null) { if (other.loginName != null) return false; } else if (!loginName.equals(other.loginName)) return false; return true; } } Logger logger = LoggerFactory.getLogger(ShiroDbRealm.class); }
自定義的類ShiroUser是為了,可以多傳輸一些內容,供後面驗證時使用,例子中只用了一個loginName,一般可以用String 傳輸就夠了。
登陸時用doGetAuthenticationInfo()函式獲得相關資訊。
登陸後訪問url 使用doGetAuthorizationInfo()獲得使用者的許可權。
5.配置檔案shiro部分
將controller的url納入許可權驗證範圍。
<?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="shiroDbRealm" class="com.blueinfo.jee.shiro.ShiroDbRealm" depends-on="userDao,roleDao">
<property name="accountService" ref="accountService"/>
</bean>
<!-- Shiro Filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/admin/login" />
<property name="successUrl" value="/admin/index" />
<property name="unauthorizedUrl" value="/admin/logout" />
<property name="filterChainDefinitions">
<value>
/admin/login = authc
/admin/logout = logout
/static/** = anon
/admin/** = authc
</value>
</property>
</bean>
<!-- 使用者授權資訊Cache, 採用EhCache -->
<bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:security/ehcache-shiro.xml"/>
</bean>
<!-- Shiro's main business-tier object for web-enabled applications -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="shiroDbRealm" />
<property name="cacheManager" ref="shiroEhcacheManager" />
</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">
<property name="proxyTargetClass" value="true" />
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
主要內容是shiroFilter的定義。
loginUrl:登陸頁面,使用者登陸不成功,自動返回此頁面。
successUrl:登陸成功後跳轉此頁面
unauthorizedUrl:使用者訪問無許可權的連結時跳轉此頁面
filterChainDefinitions:設定url的訪問許可權。anon表示不用驗證,都可以訪問。anthc:authc filter 監聽,不登陸不能訪問。logout:logout filter監聽。沒有列出的常用配置:perms["remote:invoke"] :需要角色romote 和許可權invoke才能訪問。roles["admin"]需要角色admin才能訪問。設定可用“,”隔開,如:
/admin/test = authc,roles[admin]
關於filter的列表:
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 |
6.配置檔案,啟用shiroFilter
上面的配置和原始碼定義了shiroFilter,在web.xml中啟用它
<!-- Shiro Security filter -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
7.使用annotation控制權限
在2中controller使用@RequiresRoles 註解控制權限,還可以是@RequirePermissions,要在方法上使用註解需要做如下配置:
1.在spring-mvc.xml中加入
<!-- 支援 Shiro對Controller的方法級AOP安全控制-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
2.出錯控制,在spring-mvc.xml中加入
<!-- 將Controller丟擲的異常轉到特定View -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.apache.shiro.authz.UnauthorizedException">error/403</prop>
<prop key="java.lang.Throwable">error/500</prop>
</props>
</property>
</bean>
將UnauthorizedException異常轉到403頁面,也可以使用其它頁面.8.參考
1,文中的內容大部分來源於開源工程,spring-side,真心感謝其作者江南白衣