shiro初識

1、什麼是Shiro 官網:http://shiro.apache.org/html

Apache Shiro是一個強大易用的Java安全框架,提供了認證、受權、加密和會話管理等功能;

認證 - 用戶身份識別,常被稱爲用戶「登陸」;

受權 - 訪問控制;

密碼加密 - 保護或隱藏數據防止被偷窺;

會話管理 - 每用戶相關的時間敏感的狀態。

對於任何一個應用程序,Shiro均可以提供全面的安全管理服務。而且相對於其餘安全框架,Shiro要簡單的多。

2、Shiro的架構介紹 首先,來了解一下Shiro的三個核心組件:Subject, SecurityManager 和 Realms. 以下圖: 輸入圖片說明java

Subject:即「當前操做用戶」。可是,在Shiro中,Subject這一律念並不只僅指人,也能夠是第三方進程、後臺賬戶(Daemon Account)或其餘相似事物。它僅僅意味着「當前跟軟件交互的東西」。但考慮到大多數目的和用途,你能夠把它認爲是Shiro的「用戶」概念。 Subject表明了當前用戶的安全操做,SecurityManager則管理全部用戶的安全操做。web

SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro經過SecurityManager來管理內部組件實例,並經過它來提供安全管理的各類服務。算法

Realm: Realm充當了Shiro與應用安全數據間的「橋樑」或者「鏈接器」。也就是說,當對用戶執行認證(登陸)和受權(訪問控制)驗證時,Shiro會從應用配置的Realm中查找用戶及其權限信息。 從這個意義上講,Realm實質上是一個安全相關的DAO:它封裝了數據源的鏈接細節,並在須要時將相關數據提供給Shiro。當配置Shiro時,你必須至少指定一個Realm,用於認證和(或)受權。配置多個Realm是能夠的,可是至少須要一個。spring

Shiro內置了能夠鏈接大量安全數據源(又名目錄)的Realm,如LDAP、關係數據庫(JDBC)、相似INI的文本配置資源以及屬性文件等。若是缺省的Realm不能知足需求,你還能夠插入表明自定義數據源的本身的Realm實現。數據庫

輸入圖片說明

除前文所講Subject、SecurityManager 、Realm三個核心組件外,Shiro主要組件還包括:apache

Authenticator :認證就是覈實用戶身份的過程。這個過程的常見例子是你們都熟悉的「用戶/密碼」組合。多數用戶在登陸軟件系統時,一般提供本身的用戶名(當事人)和支持他們的密碼(證書)。若是存儲在系統中的密碼(或密碼錶示)與用戶提供的匹配,他們就被認爲經過認證。編程

Authorizer :受權實質上就是訪問控制 - 控制用戶可以訪問應用中的哪些內容,好比資源、Web頁面等等。緩存

SessionManager :在安全框架領域,Apache Shiro提供了一些獨特的東西:可在任何應用或架構層一致地使用Session API。即,Shiro爲任何應用提供了一個會話編程範式 - 從小型後臺獨立應用到大型集羣Web應用。這意味着,那些但願使用會話的應用開發者,沒必要被迫使用Servlet或EJB容器了。或者,若是正在使用這些容器,開發者如今也能夠選擇使用在任何層統一一致的會話API,取代Servlet或EJB機制。安全

CacheManager :對Shiro的其餘組件提供緩存支持。

3、代碼整合: 1)、pom中加入shiro所需jar ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-aspectj</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-cas</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency>

```

2)、web.xml配置shiro攔截器及加載shiro配置文件:

<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			classpath*:spring-context*.xml
			classpath:shiro-context.xml
		</param-value>
	</context-param>

        <!-- 確保Web項目中須要權限管理的URL均可以被Shiro攔截過濾 -->
	<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>
	</filter-mapping>

3)、編寫shiro配置文件:

<?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:util="http://www.springframework.org/schema/util"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
	   http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"
	default-lazy-init="true">
	
	<!-- shiro -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean> 
	<!-- 數據庫保存的密碼是使用MD5算法加密的,因此這裏須要配置一個密碼匹配對象 -->  
    <!-- <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.Md5CredentialsMatcher"></bean>  -->
    <!-- 緩存管理 -->  
    <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"></bean> 
	<!--   
	      使用Shiro自帶的JdbcRealm類  
	      指定密碼匹配所須要用到的加密對象  
	      指定存儲用戶、角色、權限許可的數據源及相關查詢語句  
     -->
	<!-- <bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">  
        <property name="credentialsMatcher" ref="credentialsMatcher"></property>  
        <property name="permissionsLookupEnabled" value="true"></property>  
        <property name="dataSource" ref="dataSource"></property>  
        <property name="authenticationQuery"  
            value="SELECT pswd FROM u_user WHERE mobileId = ?"></property>  
        <property name="userRolesQuery"  
            value="select u_role.name from u_role,u_user_role,u_user where u_role.id = u_user_role.rid and u_user_role.uid = u_user.id and u_user.mobileId = ?"></property>  
        <property name="permissionsQuery"  
            value="select u_permission.* from u_permission,u_role_permission,u_role where u_permission.id=u_role_permission.pid and u_role_permission.rid = u_role.id and u_role.`name` = ?"></property>  
    </bean> -->
    
    <!-- Realm實現 -->
    <bean id="userRealm" class="com.company.project.common.shiro.UserRealm"></bean>
    
    
    <!-- Shiro安全管理器 -->  
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
        <property name="realm" ref="userRealm"></property>  
        <property name="cacheManager" ref="cacheManager"></property>  
    </bean>
    <!--   
       Shiro主過濾器自己功能十分強大,其強大之處就在於它支持任何基於URL路徑表達式的、自定義的過濾器的執行  
       Web應用中,Shiro可控制的Web請求必須通過Shiro主過濾器的攔截,Shiro對基於Spring的Web應用提供了完美的支持   
    -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
        <!-- Shiro的核心安全接口,這個屬性是必須的 -->  
        <property name="securityManager" ref="securityManager"></property>  
        <!-- 要求登陸時的連接(登陸頁面地址),非必須的屬性,默認會自動尋找Web工程根目錄下的"/login.jsp"頁面 -->  
        <property name="loginUrl" value="/company/login"></property>  
        <!-- 登陸成功後要跳轉的鏈接(本例中此屬性用不到,由於登陸成功後的處理邏輯在LoginController裏硬編碼) -->  
        <!-- <property name="successUrl" value="/" ></property> -->  
        <!-- 用戶訪問未對其受權的資源時,所顯示的鏈接 -->  
        <property name="unauthorizedUrl" value="/"></property>  
        <property name="filterChainDefinitions">  
            <value>  
            	/company/login=anon
                /company/subLogin=anon
                /company/**=authc
            </value>  
        </property>  
    </bean>
	<!--   
       開啓Shiro的註解(如@RequiresRoles,@RequiresPermissions),需藉助SpringAOP掃描使用Shiro註解的類,  
       並在必要時進行安全邏輯驗證  
    -->  
    <!--  
    <bean  
        class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>  
    <bean  
        class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
        <property name="securityManager" ref="securityManager"></property>  
    </bean>  
    -->
</beans>

4)、編寫自定義Realm實現類,該類繼承自AuthorizingRealm,實現其接口

package com.company.project.common.shiro;

import java.util.Date;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
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.springframework.beans.factory.annotation.Autowired;

import com.company.project.common.shiro.token.ShiroToken;
import com.company.project.common.utils.Constants;
import com.company.project.common.utils.DateUtils;
import com.company.project.modules.user.entity.User;
import com.company.project.modules.user.service.PermissionService;
import com.company.project.modules.user.service.RoleService;
import com.company.project.modules.user.service.UserService;

/**
 * 自定義shiro的Realm
 * @author Sorin
 *
 */
public class UserRealm extends AuthorizingRealm{

	@Autowired
	private UserService userService;
	
	@Autowired
	private PermissionService permissionService;
	
	@Autowired
	private RoleService roleService;
	
	/**
	 * 該類的做用是爲shiro提供認證數據源
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		ShiroToken token = (ShiroToken) authcToken;
		User user = userService.subLogin(token.getUsername(), token.getPswd());
		if(user == null){
			throw new UnknownAccountException("賬號或密碼不正確!");// 沒找到賬號
		}else if(Constants.MobileCode.ISTRUE== user.getStatus()){
			throw new DisabledAccountException("賬號已經禁止登陸!");
		}else{//更新最後登陸時間
			user.setLastLoginTime(DateUtils.getFormattedString(new Date()));
			userService.update(user);
		}
		return new SimpleAuthenticationInfo(user.getMobileId(), // 用戶名
				user.getPswd(), // 密碼
				getName() // realm name
		);
	}
	
	/**
	 * 拼裝shiro的受權數據源
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		String mobileId = (String)principals.getPrimaryPrincipal();
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		//角色
		Set<String> findRoles = roleService.findRoles(mobileId);
		String roleName = null;
		for (String role : findRoles) {
			roleName = role;
		}
		authorizationInfo.setRoles(findRoles);
		//權限
		authorizationInfo.setStringPermissions(permissionService.findPermission(roleName));
		return authorizationInfo;
	}

	

}

6)、自定義ShiroToken實體類,繼承UsernamePasswordToken

package com.company.project.common.shiro.token;

import org.apache.shiro.authc.UsernamePasswordToken;

public class ShiroToken extends UsernamePasswordToken  implements java.io.Serializable{

	private static final long serialVersionUID = -6451794657814516274L;

	public ShiroToken(String username, String pswd) {
		super(username,pswd);
		this.pswd = pswd ;
	}
	
	
	/** 登陸密碼[字符串類型] 由於父類是char[] ] **/
	private String pswd ;

	public String getPswd() {
		return pswd;
	}


	public void setPswd(String pswd) {
		this.pswd = pswd;
	}
}

7)、TokenManager類,調用shiro認證的service

public static String login(LoginVo user){
		ShiroToken token = new ShiroToken(user.getMobileId(), user.getPswd());
		token.setRememberMe(user.getIsRememberMe());
		SecurityUtils.getSubject().login(token);
		return getToken();
	}

    public static String getToken(){
		return (String)SecurityUtils.getSubject().getPrincipal();
	}

8)、LoginController類

//捕捉相應的異常進行處理便可
try{
    String mobileId = TokenManager.login(loginVo);
}catch (DisabledAccountException e) {
	e.printStackTrace();
	return resultBase("賬號已經禁用。", Constants.ResponseResultFlag.ERROR);
} catch (Exception e) {
    e.printStackTrace();
    return resultBase("賬號或密碼錯誤。", Constants.ResponseResultFlag.ERROR);
}

9)、shiro標籤使用:

<%--shiro 標籤 --%>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> 
....

<%--擁有 角色(管理員) || (用戶中心)--%>
<shiro:hasAnyRoles name='系統管理員,用戶中心'>
<shiro:hasPermission name="/member/list.shtml">
<li><a href="/member/list.shtml">用戶列表</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="/member/online.shtml">
<li><a href="/member/online.shtml">在線用戶</a></li>
</shiro:hasPermission>
相關文章
相關標籤/搜索