1. 程式人生 > >Shiro-認證思路分析及認證流程實現

Shiro-認證思路分析及認證流程實現

1、獲取當前的Subject。呼叫SecurityUtils.getSubject();
2、測試當前的使用者是否已經被認證。即是否已經登入。呼叫Subject的isAuthenticated()
3、若沒有被認證,則把使用者名稱和密碼封裝為UsernamePasswordToken物件
    1)建立一個表單頁面
    2)把請求提交到SpringMVC的Handler
    3)獲取使用者名稱和密碼
4、執行登入:呼叫Subject的login(AuthenticationToken)方法。
5、自定義Realm的方法,從資料庫中獲取對應的記錄,返回給Shiro。
    1)實際上需要繼承org.apache.shiro.realm.AuthenticatingRealm類,org.apache.shiro.realm.AuthenticatingRealm間接實現了Realm介面
    2)實現doGetAuthenticationInfo(AuthenticationToken)方法。
6、由shiro完成對密碼的比對。

專案結構

1、整合spirng和shiro

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">

	<!-- needed for ContextLoaderListener -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>

	<!-- Bootstraps the root web application context before servlet initialization -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- 配置shiro的過濾器 -->
	<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>


	<!-- The front controller of this Spring Web application, responsible for 
		handling all application requests -->
	<servlet>
		<servlet-name>spring</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>spring</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

</web-app>

spring配置檔案

<?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.xsd">


	<!-- 配置SecurityManager -->
	<bean id="securityManager"
		class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="cacheManager" ref="cacheManager" />
		<!-- Single realm app. If you have multiple realms, use the 'realms' property 
			instead. -->
		<property name="sessionMode" value="native" />
		<property name="realm" ref="jdbcRealm" />
	</bean>

	<!-- 配置cacheManager
			需要加入ehcache的配置檔案和jar包
	 -->
	<bean id="cacheManager"	class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
	</bean>

	<!-- Used by the SecurityManager to access security data (users, roles, 
		etc). Many other realm implementations can be used too (PropertiesRealm, 
		LdapRealm, etc. -->
	<!-- 直接使用實現了Realm介面的bean -->
	<bean id="jdbcRealm" class="com.atguigu.shiro.realms.ShiroRealm"></bean>

	<!-- ========================================================= Shiro Spring-specific integration ========================================================= -->
	<!-- Post processor that automatically invokes init() and destroy() methods 
		for Spring-configured Shiro objects so you don't have to 1) specify an init-method 
		and destroy-method attributes for every bean definition and 2) even know 
		which Shiro objects require these methods to be called. -->
	<!-- 配置生命週期的後置處理器,自動呼叫spirng的IOC容器中shiro的方法 -->
	<bean id="lifecycleBeanPostProcessor"
		class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

	<!-- Enable Shiro Annotations for Spring-configured beans. Only run after 
		the lifecycleBeanProcessor has run: 
		啟用shiro的生命週期註解,其依賴lifecycleBeanPostProcessor,所以在配置了lifecycleBeanPostProcessor之後才能生效	
	-->
	<bean
		class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
		depends-on="lifecycleBeanPostProcessor" />
	<bean
		class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager" />
	</bean>

	<!--
		這裡的id必須於web.xml檔案中配置的DelegatingFilterProxy的filter-name一樣
		若不一樣則丟擲NoSuchBeanDefinitionException 
		因為shiro會到spring的IOC容器中找shiroFilter對應的bean
		
		若不一致的話也可以在fileter的初始化引數中配置targetBeanName,將這裡的shiroFilter換成targetBeanName的值就可以了
	-->
	<bean id="shiroFilter"
		class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<property name="loginUrl" value="/login.jsp" />
		<property name="successUrl" value="/list.jsp" />
		<property name="unauthorizedUrl" value="/unauthorized.jsp" />
		<!-- 
			配置哪些頁面需要保護
			以及訪問頁面的許可權
			攔截器:(這裡的url支援Ant風格模式)
			1).anon 可以被匿名訪問
			2) .authc 需要認證才能訪問
			這裡的url優先匹配
		 -->
		<property name="filterChainDefinitions">
			<value>
				/login.jsp = anon
				/shiro/login = anon
				
				# everything else requires authentication:
				/** = authc
				
				/list.jsp = anon
			</value>
		</property>
	</bean>


</beans>

ehcache.xml

<ehcache>

    <!-- Sets the path to the directory where cache .data files are created.

         If the path is a Java System Property it is replaced by
         its value in the running VM.

         The following properties are translated:
         user.home - User's home directory
         user.dir - User's current working directory
         java.io.tmpdir - Default temp file path -->
    <diskStore path="java.io.tmpdir"/>


    <!--Default Cache configuration. These will applied to caches programmatically created through
        the CacheManager.

        The following attributes are required for defaultCache:

        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.

        -->
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />

    <!--Predefined caches.  Add your cache configuration settings here.
        If you do not have a configuration for your cache a WARNING will be issued when the
        CacheManager starts

        The following attributes are required for defaultCache:

        name              - Sets the name of the cache. This is used to identify the cache. It must be unique.
        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.

        -->

    <!-- Sample cache named sampleCache1
        This cache contains a maximum in memory of 10000 elements, and will expire
        an element if it is idle for more than 5 minutes and lives for more than
        10 minutes.

        If there are more than 10000 elements it will overflow to the
        disk cache, which in this configuration will go to wherever java.io.tmp is
        defined on your system. On a standard Linux system this will be /tmp"
        -->
    <cache name="sampleCache1"
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
        />

    <!-- Sample cache named sampleCache2
        This cache contains 1000 elements. Elements will always be held in memory.
        They are not expired. -->
    <cache name="sampleCache2"
        maxElementsInMemory="1000"
        eternal="true"
        timeToIdleSeconds="0"
        timeToLiveSeconds="0"
        overflowToDisk="false"
        /> -->

    <!-- Place configuration for your caches following -->

</ehcache>

2、編碼實現

login.jsp

	<form action="shiro/login" method="post">
		UserName:<input name="username" type="text"/>
		
		<br><br>
		
		PassWord:<input name="password" type="password"/>
		
		<br><br>
		
		<input type="submit" value="Submit"/>
	</form>

ShiroHandler.java

package com.atguigu.shiro.handlers;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * 實現Shiro認證
 * @author Lee
 *
 */
@Controller
@RequestMapping("/shiro")
public class ShiroHandler {

	@RequestMapping(value="/login",method=RequestMethod.POST)
	public String login(@RequestParam("username") String username,@RequestParam("password") String password) {
		System.out.println(username + "" + password);
		Subject currentUser = SecurityUtils.getSubject();
		
		if (!currentUser.isAuthenticated()) {
			UsernamePasswordToken token = new UsernamePasswordToken(username, password);
			token.setRememberMe(true);
			System.out.println(token.hashCode());
			try {
				currentUser.login(token);
			} catch (AuthenticationException e) {
				System.out.println("登陸失敗");
				e.printStackTrace();
			}
		}
		return "redirect:/list.jsp";
	}
	
}

ShiroRealm.java

package com.atguigu.shiro.realms;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.AuthenticatingRealm;

/**
 * 
 * @author Lee
 *
 */
public class ShiroRealm extends AuthenticatingRealm{

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("doGetAuthenticationInfo" + token);
		System.out.println(token.hashCode());
		return null;
	}

}