認證政策
用法
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" />
<!-- 這裡是将多個Realm配置在實作了 AbstractAuthenticator 的org.apache.shiro.authc.pam.ModularRealmAuthenticator中 ,由ModularRealmAuthenticator進行認證-->
<property name="authenticator" ref="authenticator"></property>
<!-- <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>
<!-- 配置多reaml的認證器 -->
<bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
<property name="realms">
<list>
<ref bean="jdbcRealm"/>
<ref bean="secondRealm"/>
</list>
</property>
</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">
<!-- 配置憑證比對器 -->
<property name="credentialsMatcher">
<!-- shiro推薦直接使用 HashedCredentialsMatcher-->
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"></property>
<property name="hashIterations" value="1024"></property>
</bean>
</property>
</bean>
<bean id="secondRealm" class="com.atguigu.shiro.realms.SecondRealm">
<!-- 配置憑證比對器 -->
<property name="credentialsMatcher">
<!-- shiro推薦直接使用 HashedCredentialsMatcher-->
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="SHA1"></property>
<property name="hashIterations" value="1024"></property>
</bean>
</property>
</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 需要認證才能通路
3) .logout 登出過濾器
這裡的url優先比對
-->
<property name="filterChainDefinitions">
<value>
/login.jsp = anon
/shiro/login = anon
/shiro/logout = logout
# everything else requires authentication:
/** = authc
/list.jsp = anon
</value>
</property>
</bean>
</beans>
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.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.util.ByteSource;
/**
*
* @author Lee
*
*/
public class ShiroRealm extends AuthenticatingRealm {
private static final String MONSTER = "monster";
private static final String UNKNOW = "unknow";
private static final String ALGORITHM_NAME = "MD5";
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("[First] doGetAuthenticationInfo");
// 1、将AuthenticationToken轉換為UsernamePasswordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
// 2、從UsernamePasswordToken中擷取username
String username = upToken.getUsername();
// char[] password = upToken.getPassword();
// 3、從資料庫中擷取username對應的記錄
System.out.println("從資料庫中查找" + username + "對應的記錄");
// 4、若使用者不存在抛出UnknownAccountException異常
if (UNKNOW.equals(username)) {
throw new UnknownAccountException("使用者不存在");
}
// 5、根據使用者的資訊決定是否抛出AuthenticationException異常
if (MONSTER.equals(username)) {
throw new LockedAccountException("使用者被鎖定");
}
// 6、根據使用者情況,建構 AuthenticationInfo 對象并傳回
// 一下資訊是從資料庫中擷取
// 1)principal:認證的實體資訊,可以是username,也可以是資料表對應的使用者的實體類對象。
Object principal = username;
// 2) credentials:密碼
Object hashedCredentials = null; // "fc1709d0a95a6be30bc5926fdb7f22f4";
if (username.equals("admin")) {
hashedCredentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
} else if (username.equals("user")) {
hashedCredentials = "098d2c478e9c11555ce2823231e02ec1";
}
// 3) realmName:目前realm對象的name,調用父類的getName()方法擷取
String realmName = getName();
// 比較密碼是由AuthenticatingRealm的CredentialsMatcher進行比較的
// 4) credentialsSalt:鹽值.
// 這裡的ByteSource為接口,裡面有内部類Util,方法中的字元串是唯一的字元串,這裡使用username,username是使用者的唯一辨別
// 唯一的字元串
ByteSource credentialsSalt = ByteSource.Util.bytes(username);
SimpleAuthenticationInfo info = null; // new SimpleAuthenticationInfo(principal, credentials, realmName);
info = new SimpleAuthenticationInfo(principal, hashedCredentials, credentialsSalt, realmName);
return info;
}
public static void main(String[] args) {
ByteSource credentialsSalt = ByteSource.Util.bytes("user");
int hashIterations = 1024;
String credentials = "123456";
SimpleHash simpleHash = new SimpleHash(ALGORITHM_NAME, credentials, credentialsSalt, hashIterations);
System.out.println(simpleHash);
}
}
SecondRealm.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.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.util.ByteSource;
/**
*
* @author Lee
*
*/
public class SecondRealm extends AuthenticatingRealm {
private static final String MONSTER = "monster";
private static final String UNKNOW = "unknow";
private static final String ALGORITHM_NAME = "SHA1";
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("[SecondRealm] doGetAuthenticationInfo");
// 1、将AuthenticationToken轉換為UsernamePasswordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
// 2、從UsernamePasswordToken中擷取username
String username = upToken.getUsername();
// char[] password = upToken.getPassword();
// 3、從資料庫中擷取username對應的記錄
System.out.println("從資料庫中查找" + username + "對應的記錄");
// 4、若使用者不存在抛出UnknownAccountException異常
if (UNKNOW.equals(username)) {
throw new UnknownAccountException("使用者不存在");
}
// 5、根據使用者的資訊決定是否抛出AuthenticationException異常
if (MONSTER.equals(username)) {
throw new LockedAccountException("使用者被鎖定");
}
// 6、根據使用者情況,建構 AuthenticationInfo 對象并傳回
// 一下資訊是從資料庫中擷取
// 1)principal:認證的實體資訊,可以是username,也可以是資料表對應的使用者的實體類對象。
Object principal = username;
// 2) credentials:密碼
Object hashedCredentials = null; // "fc1709d0a95a6be30bc5926fdb7f22f4";
if (username.equals("admin")) {
hashedCredentials = "ce2f6417c7e1d32c1d81a797ee0b499f87c5de06--";
} else if (username.equals("user")) {
hashedCredentials = "073d4c3ae812935f23cb3f2a71943f49e082a718--";
}
// 3) realmName:目前realm對象的name,調用父類的getName()方法擷取
String realmName = getName();
// 比較密碼是由AuthenticatingRealm的CredentialsMatcher進行比較的
// 4) credentialsSalt:鹽值.
// 這裡的ByteSource為接口,裡面有内部類Util,方法中的字元串是唯一的字元串,這裡使用username,username是使用者的唯一辨別
// 唯一的字元串
ByteSource credentialsSalt = ByteSource.Util.bytes(username);
SimpleAuthenticationInfo info = null; // new SimpleAuthenticationInfo(principal, credentials, realmName);
info = new SimpleAuthenticationInfo("SecondRealm", hashedCredentials, credentialsSalt, realmName);
return info;
}
public static void main(String[] args) {
ByteSource credentialsSalt = ByteSource.Util.bytes("admin");
int hashIterations = 1024;
String credentials = "123456";
SimpleHash simpleHash = new SimpleHash(ALGORITHM_NAME, credentials, credentialsSalt, hashIterations);
System.out.println(simpleHash);
}
}