springMVC+shiro+mybatis

shiro介紹

一、什麼是shiro

shiro是apache旗下的一個開源框架,它將軟件系統的安全認證相關的功能抽取出來,實現用戶身份認證,授權、加密、會話管理等功能,組成了一個通用的安全認證框架。Shiro可以非常容易的開發出足夠好的應用,其不僅可以用在JavaSE環境,也可以用在JavaEE環境,它相當簡單。

二、Apache Shiro 的架構:


其中三大核心:

1、Subject :當前用戶的操作

Subject即主體,外部應用與subject進行交互,subject記錄了當前操作用戶,將用戶的概念理解爲當前操作的主體,可能是一個通過器請求的用戶,也
可能是一個運行的程序。Subject在shiro中是一個接口,接口中定義了很多認證授相關的方法,外部程序通過subject進行認證授,而subject是通過
SecurityManager安全管理器進行認證授

2、SecurityManager:用於管理所有的Subject

SecurityManager即安全管理器,對全部的subject進行安全管理,它是shiro的核心,負責對所有的subject進行安全管理。通過
SecurityManager可以完成subject的認證、授權等,實質上SecurityManager是通過Authenticator進行認證,通過Authorizer進行授權,

通過SessionManager進行會話管理等。

SecurityManager是一個接口,繼承了Authenticator, Authorizer, SessionManager這三個接口。

3、Realms:用於進行權限信息的驗證

Realm即領域,相當於datasource數據源,securityManager進行安全認證需要通過Realm獲取用戶權限數據,
比如:如果用戶身份數據在數據庫那麼realm就需要從數據庫獲取用戶身份信息。
注意:不要把realm理解成只是從數據源取數據,在realm中還有認證授權校驗的相關的代碼。
其他組件:

 4、Authenticator:認證器,驗證用戶身份的過程.。

對用戶身份進行認證,Authenticator是一個藉口,shiro提供ModularRealmAuthenticator實現類,通過ModularRealmAuthenticator基本可以滿 足大多數需求,也可以自定義認證器。

5、Authorizer:授權器,授權訪問控制。

用戶通過認證器認證通過,在訪問功能時需要通過授權器判斷用戶是否有此功能的操作權限。

6、sessionManager:會話管理

shiro框架定義了一套會話管理,它不依賴web容器的session,所以shiro可以使用在非web應用上,也可以將分佈式應用的會話集中在一點管 理,此特性可使它實現單點登錄。

7、SessionDao:會話dao

是對session會話操作的一套接口,比如要將session存儲到數據庫,可以通過jdbc將會話存儲到數據庫。

8、CacheManager:緩存管理

緩存管理,將用戶權限數據存儲在緩存,這樣可以提高性能。

9、Cryptography:密碼管理

shiro提供了一套加密/解密的組件,方便開發。比如提供常用的散列、加/解密等功能。

三、認證流程和授權流程

認證流程:
構造SecurityManager環境——》Subject.login()提交認證——》SecurityManager.login()執行認證——》Authenticator執行認證——》Realm根據身份獲取驗證信息。
授權流程:
構造SecurityManager環境——》Subject.isPermitted()授權——》SecurityManager.isPermitted()執行授權——》Authorizer執行授權——》Realm根據身份獲
取資源權限信息

四、shiro與SpringMVC整合

數據庫:
CREATE TABLE `sys_permission` (
  `id` bigint(20) NOT NULL COMMENT '主鍵',
  `name` varchar(128) NOT NULL COMMENT '資源名稱',
  `type` varchar(32) NOT NULL COMMENT '資源類型:menu,菜單 permission菜單下的資源,',
  `url` varchar(128) DEFAULT NULL COMMENT '訪問url地址',
  `percode` varchar(128) DEFAULT NULL COMMENT '權限代碼字符串',
  `parentid` bigint(20) DEFAULT NULL COMMENT '父結點id',
  `parentids` varchar(128) DEFAULT NULL COMMENT '父結點id列表串',
  `sortstring` varchar(128) DEFAULT NULL COMMENT '排序號',
  `available` char(1) DEFAULT NULL COMMENT '是否可用,1:可用,0不可用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


1.在web.xml添加shiroFilter

用shiro來管理攔截器
<!-- shiro過慮器,DelegatingFilterProx會從spring容器中找shiroFilter -->
	<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>

2.配置applicationContext-shiro.xml

並配置到web.xml的contextConfigLocation中

<!-- Shiro 的Web過濾器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<!-- 如果沒有認證將要跳轉的登陸地址,http可訪問的url,如果不在表單認證過慮器FormAuthenticationFilter中指定此地址就爲身份認證地址 -->
		<property name="loginUrl" value="/login.action" />
		<!-- 沒有權限跳轉的地址 -->
		<property name="unauthorizedUrl" value="/refuse.jsp" />
		<!-- shiro攔截器配置 -->
		<property name="filters">
			<map>
				<entry key="authc" value-ref="formAuthenticationFilter" />
			</map>
		</property>
		<property name="filterChainDefinitions">
			<value>
				<!-- 必須通過身份認證方可訪問,身份認 證的url必須和過慮器中指定的loginUrl一致 -->
				/loginsubmit.action = authc
				<!-- 退出攔截,請求logout.action執行退出操作 -->
				/logout.action = logout
				<!-- 無權訪問頁面 -->
				/refuse.jsp = anon
				<!-- roles[XX]表示有XX角色纔可訪問 -->
				/item/list.action = roles[item],authc
				/js/** anon
				/images/** anon
				/styles/** anon
				<!-- user表示身份認證通過或通過記住我認證通過的可以訪問 -->
				/** = user
				<!-- /**放在最下邊,如果一個url有多個過慮器則多個過慮器中間用逗號分隔,如:/** = user,roles[admin] -->

			</value>
		</property>
	</bean>


	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="userRealm" />
		
	</bean>

	<!-- 自定義 realm -->
	<bean id="userRealm" class="cn.ssm.realm.CustomRealm1">
	</bean>
	<!-- 基於Form表單的身份驗證過濾器,不配置將也會註冊此過慮器,表單中的用戶賬號、密碼及loginurl將採用默認值,建議配置 -->
	<bean id="formAuthenticationFilter"
		class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
		<!-- 表單中賬號的input名稱 -->
		<property name="usernameParam" value="usercode" />
		<!-- 表單中密碼的input名稱 -->
		<property name="passwordParam" value="password" />
		<!-- <property name="rememberMeParam" value="rememberMe"/> -->
		<!-- loginurl:用戶登陸地址,此地址是可以http訪問的url地址 -->
		<property name="loginUrl" value="/loginsubmit.action" />
	</bean>
securityManager:這個屬性是必須的。
loginUrl:沒有登錄認證的用戶請求將跳轉到此地址,不是必須的屬性,不輸入地址的話會自動尋找項目web項目的根目錄下的”/login.jsp”頁面。
unauthorizedUrl:沒有權限默認跳轉的頁面。

3.自定義realm

用戶ActiveUser
public class ActiveUser {
	private String userid;//用戶id(主鍵)
	private String usercode;// 用戶賬號
	private String username;// 用戶名稱

	private List<SysPermission> menus;// 菜單
	private List<SysPermission> permissions;// 權限


自定義realm
public class CustomRealm1 extends AuthorizingRealm {

	@Autowired
	private SysService sysService;

	@Override
	public String getName() {
		return "customRealm";
	}

	// 支持什麼類型的token
	@Override
	public boolean supports(AuthenticationToken token) {
		return token instanceof UsernamePasswordToken;
	}

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		// 從token中獲取用戶身份
		String usercode = (String) token.getPrincipal();

		SysUser sysUser = null;
		try {
			sysUser = sysService.findSysuserByUsercode(usercode);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 如果賬號不存在
		if (sysUser == null) {
			throw new UnknownAccountException("賬號找不到");
		}

		// 根據用戶id取出菜單
		List<SysPermission> menus = null;
		try {
			menus = sysService.findMenuList(sysUser.getId());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 用戶密碼
		String password = sysUser.getPassword();
		//鹽
		String salt = sysUser.getSalt();
		
		// 構建用戶身體份信息
		ActiveUser activeUser = new ActiveUser();
		activeUser.setUserid(sysUser.getId());
		activeUser.setUsername(sysUser.getUsername());
		activeUser.setUsercode(sysUser.getUsercode());
		activeUser.setMenus(menus);
		
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
				activeUser, password, ByteSource.Util.bytes(salt),getName());
		
		return simpleAuthenticationInfo;
	}

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		
		//身份信息
		ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
		//用戶id
		String userid = activeUser.getUserid();
		//獲取用戶權限
		List<SysPermission> permissions = null;
		try {
			permissions = sysService.findSysPermissionList(userid);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//構建shiro授權信息
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		for(SysPermission sysPermission:permissions){
			simpleAuthorizationInfo.addStringPermission(sysPermission.getPercode());
		}
		
		return simpleAuthorizationInfo;
		
	}

}

5.添加憑證匹配器

添加憑證匹配器實現md5加密校驗。applicationContext-shiro.xml添加:
<!-- 憑證匹配器 -->
	<bean id="credentialsMatcher"
		class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
		<property name="hashAlgorithmName" value="md5" />
		<property name="hashIterations" value="1" />
	</bean>

<!-- 自定義 realm -->
	<bean id="userRealm" class="cn.ssm.realm.CustomRealm1">
		<property name="credentialsMatcher" ref="credentialsMatcher" />
	</bean>

6.開啓對shiro註解支持

<!-- 開啓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>
在Controller中添加
@RequiresPermissions("item:query")
其中item:query是數據庫中的權限代碼字符串,在授權的時候會查詢數據庫進行查找。

7.配置緩存

在applicationContextr-shiro.xml中配置緩存
<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="userRealm" />
		<property name="sessionManager" ref="sessionManager" />
		<property name="cacheManager" ref="cacheManager"/>
	</bean>

<!-- 緩存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    	<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
    </bean>
shiro-ehcache.xml:僅供參考
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
	<defaultCache 
		maxElementsInMemory="1000" 
		maxElementsOnDisk="10000000"
		eternal="false" 
		overflowToDisk="false" 
		diskPersistent="false"
		timeToIdleSeconds="120"
		timeToLiveSeconds="120" 
		diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
	</defaultCache>
</ehcache>

8.配置session管理

在applicationContextr-shiro.xml中配置session
<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="userRealm" />
		<property name="sessionManager" ref="sessionManager" />
	</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>

9.登錄退出

登錄;
//登陸提交地址,和applicationContext-shiro.xml中配置的loginurl一致
	@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";
	}	
//系統首頁
	@RequestMapping("/first")
	public String first(Model model)throws Exception{
		
		//從shiro的session中取activeUser
		Subject subject = SecurityUtils.getSubject();
		//取身份信息
		ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
		//通過model傳到頁面
		model.addAttribute("activeUser", activeUser);
		
		return "/first";
	}
退出:不需要寫退出,直接在shiro配置中添加,session由shiro管理。可以直接退出。


10.shiro過濾器和jsp標籤


過濾器:
過濾器簡稱 對應的java類
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
logout org.apache.shiro.web.filter.authc.LogoutFilter




anon:例子/admins/**=anon 沒有參數,表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要認證(登錄)才能使用,沒有參數
roles:例子/admins/user/**=roles[admin],參數可以寫多個,多個時必須加上引號,並且參數之間用逗號分割,當有多個參數時,例如admins/user/**=roles["admin,guest"],每個參數通過纔算通過,相當於hasAllRoles()方法。
perms:例子/admins/user/**=perms[user:add:*],參數可以寫多個,多個時必須加上引號,並且參數之間用逗號分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],當有多個參數時必須每個參數都通過才通過,想當於isPermitedAll()方法。
rest:例子/admins/user/**=rest[user],根據請求的方法,相當於/admins/user/**=perms[user:method] ,其中method爲post,get,delete等。
port:例子/admins/user/**=port[8081],當請求的url的端口不是8081是跳轉到schemal://serverName:8081?queryString,其中schmal是協議http或https等,serverName是你訪問的host,8081是url配置裏port的端口,queryString
是你訪問的url裏的?後面的參數。
authcBasic:例如/admins/user/**=authcBasic沒有參數表示httpBasic認證


ssl:例子/admins/user/**=ssl沒有參數,表示安全的url請求,協議爲https
user:例如/admins/user/**=user沒有參數表示必須存在用戶,當登入操作時不做檢查
注:
anon,authcBasic,auchc,user是認證過濾器,
perms,roles,ssl,rest,port是授權過濾器

jsp標籤:
Jsp頁面添加:
<%@ tagliburi="http://shiro.apache.org/tags" prefix="shiro" %>


標籤名稱 標籤條件(均是顯示標籤內容)
<shiro:authenticated> 登錄之後
<shiro:notAuthenticated> 不在登錄狀態時
<shiro:guest> 用戶在沒有RememberMe時
<shiro:user> 用戶在RememberMe時
<shiro:hasAnyRoles name="abc,123" > 在有abc或者123角色時
<shiro:hasRole name="abc"> 擁有角色abc
<shiro:lacksRole name="abc"> 沒有角色abc
<shiro:hasPermission name="abc"> 擁有權限資源abc
<shiro:lacksPermission name="abc"> 沒有abc權限資源
<shiro:principal> 顯示用戶身份名稱
 <shiro:principal property="username"/>     顯示用戶身份中的屬性值


以上就是shiro和springmvc初步整合。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章