許可權控制器——Shiro安全框架
阿新 • • 發佈:2019-01-03
【Shiro框架】
它是一種安全框架,用於解決系統認證和授權問題,同時提供了會化管理,資料加密機制。
傳統的登入:
Shiro安全框架實現登
什麼情況下使用Shiro框架:
使用者登入時:檢測使用者是否登入正確、如登入錯誤或未登入狀態、直接跳轉到登入頁面並給出提示。如果使用者未登入直接訪問後臺,Shiro框架可以根據使用者的請求給出相應的攔截, 並進行跳轉到登入頁面,防止使用者未登入狀態下訪問到系統的核心功能區。假如使用者登入資訊認證通過,Shiro框架可以根據使用者的不同角色身份,不同的許可權進而系統中顯示能夠操作模組也不相同。這就是Shiro所謂的先認證後授權。
如何使用安全框架:
程式設計師只關注兩部分:
1.如何獲得Subject
2.如何定義一個符合規定的Realm域(密碼比較器也是我們自己乾的)
maven功能導包操作:
<!-- shiro --> <!-- apache shiro dependencies --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.2.3</version> </dependency>
用Shiro安全框架首先配置web.xml
<!-- Shiro Security filter filter-name這個名字的值將來還會在spring中用到 一定要放到struts2核心過濾器之前 --> <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>
產生代理類的方式:在applicationContext.xml中進行配置
<!-- Shiro產生代理的方式 :下面程式碼說明了使用cglib方式來生成代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--引入shiro.xml檔案-->
<import resource="classpath:spring/applicationContext-shiro.xml"/>
配置applicationContext-shiro.xml檔案
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<description>Shiro的配置</description>
<!-- SecurityManager配置 -->
<!-- 配置Realm域 -->
<!-- 密碼比較器 -->
<!-- 代理如何生成? 用工廠來生成Shiro的相關過濾器-->
<!-- 配置快取:ehcache快取 -->
<!-- 安全管理 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- Single realm app. If you have multiple realms, use the 'realms' property instead. -->
<property name="realm" ref="authRealm"/><!-- 引用自定義的realm -->
<!-- 快取 -->
<property name="cacheManager" ref="shiroEhcacheManager"/>
</bean>
<!-- 自定義許可權認證 -->
<bean id="authRealm" class="cn.itcast.jk.shiro.AuthRealm">
<property name="userService" ref="userService"/>
<!-- 自定義密碼加密演算法 -->
<property name="credentialsMatcher" ref="passwordMatcher"/>
</bean>
<!-- 設定密碼加密策略 md5hash -->
<bean id="passwordMatcher" class="cn.itcast.jk.shiro.CustomCredentialsMatcher"/>
<!-- filter-name這個名字的值來自於web.xml中filter的名字 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!--登入頁面 -->
<property name="loginUrl" value="/index.jsp"></property>
<!-- 登入成功後 -->
<property name="successUrl" value="/home.action"></property>
<property name="filterChainDefinitions">
<!-- /**代表下面的多級目錄也過濾 -->
<value>
/index.jsp* = anon <!--過濾器名-->
/home* = anon
/sysadmin/login/login.jsp* = anon
/sysadmin/login/logout.jsp* = anon
/login* = anon
/logout* = anon
/components/** = anon
/css/** = anon
/images/** = anon
/js/** = anon
/make/** = anon
/skin/** = anon
/stat/** = anon
/ufiles/** = anon
/validator/** = anon
/resource/** = anon
/** = authc
/*.* = authc <!--過濾器-->
</value>
</property>
</bean>
<!-- 使用者授權/認證資訊Cache, 採用EhCache 快取 -->
<bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>
</bean>
<!-- 保證實現了Shiro內部lifecycle函式的bean執行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 生成代理,通過代理進行控制 -->
<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>
Md5Hash加密工具類
package cn.itcast.jk.utils;
import org.apache.shiro.crypto.hash.Md5Hash;
/**
* @Description:
* @Author: 周家林
* @CreateDate: 2016年12月4日
*/
public class Encrypt {
/*
* 雜湊演算法一般用於生成資料的摘要資訊,是一種不可逆的演算法,一般適合儲存密碼之類的資料,
* 常見的雜湊演算法如MD5、SHA等。一般進行雜湊時最好提供一個salt(鹽),比如加密密碼“admin”,
* 產生的雜湊值是“21232f297a57a5a743894a0e4a801fc3”,
* 可以到一些md5解密網站很容易的通過雜湊值得到密碼“admin”,
* 即如果直接對密碼進行雜湊相對來說破解更容易,此時我們可以加一些只有系統知道的干擾資料,
* 如使用者名稱和ID(即鹽);這樣雜湊的物件是“密碼+使用者名稱+ID”,這樣生成的雜湊值相對來說更難破解。
*/
//高強度加密演算法,不可逆
public static String md5(String password, String salt){
return new Md5Hash(password,salt,2).toString();
}
public static void main(String[] args) {
System.out.println(new Md5Hash("123456","tony",2).toString());
}
}
編寫Realm域
package cn.itcast.jk.shiro;
import java.util.ArrayList;
import java.util.List;
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.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
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 cn.itcast.jk.domain.Module;
import cn.itcast.jk.domain.Role;
import cn.itcast.jk.domain.User;
import cn.itcast.jk.service.UserService;
/**
* 自定義了一個Realm域 (主要作用是提供安全資料:使用者,角色,模組)
* @author Administrator
*
*/
public class AuthRealm extends AuthorizingRealm {
//注入userService
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
//授權
/**
* 驗證使用者是否具有某某許可權
*
* 當jsp頁面上碰到Shiro標籤時就會呼叫這個方法,當第一次碰到時才呼叫這個方法
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
//1.得到使用者資訊
User user= (User) pc.fromRealm(this.getName()).iterator().next();
//2.通過物件導航得到使用者的角色列表
Set<Role> roles = user.getRoles();
List<String> permissions = new ArrayList<String>();
//遍歷角色列表,得到每個使用者的角色
for(Role role:roles){
//得到每個角色,並通過物件導航,進一步載入這個角色下的模組
Set<Module> modules = role.getModules();
//遍歷模組的集合,得到每個模組的資訊
for(Module module:modules){
permissions.add(module.getName());
}
}
//宣告AuthorizationInfo的一個子類物件
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permissions);
return info;
}
/**
* 認證(在登入時就會呼叫這個方法) Subject.login();
* 引數:AuthenticationToken代表使用者在介面上輸入的使用者名稱和密碼
* 返回值不為null就會執行密碼比較器
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1.將token轉化為子類物件
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2.從token中獲取使用者介面輸入的使用者名稱
String username = upToken.getUsername();
//3.呼叫業務邏輯層,根據使用者名稱查詢使用者物件
List<User> userList = userService.find("from User where userName=?", User.class, new String[]{username});
if(userList!=null && userList.size()>0){
//查詢到了使用者物件,說明了使用者名稱是正確的
User user = userList.get(0);
//principal代表使用者資訊 credentials代表使用者的密碼 第三個引數:只要是一個字串就可以
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
return info;
}
//4.組織返回的結果
return null;
}
}
已在applicationContext-shiro.xml中進行了配置。如下:
<!-- 自定義許可權認證 -->
<bean id="authRealm" class="cn.itcast.jk.shiro.AuthRealm">
<property name="userService" ref="userService"/>
<!-- 自定義密碼加密演算法 -->
<property name="credentialsMatcher" ref="passwordMatcher"/>
</bean>
<!-- 設定密碼加密策略 md5hash -->
<bean id="passwordMatcher" class="cn.itcast.jk.shiro.CustomCredentialsMatcher"/>
密碼比較器CustomCredentialsMatcher
package cn.itcast.jk.shiro;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import cn.itcast.jk.utils.Encrypt;
/**
* 自定義的密碼比較器
* @author 周家林
*
*/
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {
/**
* 重寫了密碼比較的方法
* 第一個引數AuthenticationToken 代表使用者在介面上輸入的使用者名稱和密碼
*
* 第二個引數AuthenticationInfo 代表了當前這個使用者在資料庫中的資訊,就會有加密後的密碼
*
* 返回值:true證明密碼比較成功了
* false證明密碼比較失敗,密碼輸入錯誤,程式會丟擲異常
*
*/
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
//1.將使用者在介面輸入的密碼進行加密
UsernamePasswordToken upToken = (UsernamePasswordToken) token; //向下轉型
String inputpwd = new String(upToken.getPassword());
String inputpwdEncrypt = Encrypt.md5(inputpwd, upToken.getUsername());//md5hash演算法進行加密
//2.將使用者在資料庫中的密碼讀取出來
String dbPwd = info.getCredentials().toString();
//3.進行比較
return super.equals(inputpwdEncrypt, dbPwd);
}
}
LoginAction中的login和logout登入程式碼
//SSH傳統登入方式
public String login() throws Exception {
//1.與Shiro互動
Subject subject = SecurityUtils.getSubject();
//如果已登入過,直接在頁面上回車
if(subject.isAuthenticated()){
return SUCCESS;
}
if(UtilFuns.isEmpty(username)){
return "login";
}
try {
//2.呼叫subject中的方法,來實現登入
UsernamePasswordToken token = new UsernamePasswordToken(username, password);//將使用者在介面輸入的使用者名稱密碼進行封裝
subject.login(token);//當login執行時,就會自動跳入authRealm域中的認證方法
//3.從Shiro中取出使用者登入結果資訊
User user = (User) subject.getPrincipal();
//4.將使用者資訊儲存到session中
session.put(SysConstant.CURRENT_USER_INFO, user);
return SUCCESS;
} catch (Exception e) {
e.printStackTrace();
request.put("errorInfo", "登入失敗,使用者名稱或密碼錯誤!");
return "login";
}
}
//退出
public String logout(){
session.remove(SysConstant.CURRENT_USER_INFO); //刪除session
SecurityUtils.getSubject().logout();//Shiro中的登入方法,才會真實的將JSESSIONID刪除
return "logout";
}
測試授權過程:
當jsp頁面上出現Shiro標籤時就會執行AuthRealm域中的授權方法
1.引入 Shiro標籤
<%@taglib lib="http:shiro.apache.org/tags" prefix="shiro"%>
2.使用shiro標籤進行授權判斷
舉例:
<shiro:hasPermission name="系統首頁">
<span id="topmenu" onclick="toModule('home');">系統首頁</span><span id="tm_separator"></span>
</shiro:hasPermission>
其實shiro標籤就和<c:if></c:if>很像,如果此使用者擁有此模組操作權,此模組就會顯示,沒有許可權,此模組就不會顯示。
小結:
這是我對Shiro安全 框架的應用展示,如有不足敬請提出,大家一起進步。