1.基礎介紹以及demo演示網(wǎng)址:
2.Spring集成Shiro【大部分情況下我們使用Shiro都會和Spring集成的】
- 把Shiro中的重要對象交給Spring管理
2.1導包(之前文檔中已經(jīng)導過)
<!-- shiro的支持包 (權(quán)限管理)-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
<type>pom</type>
</dependency>
<!-- shiro與Spring的集成包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
2.2 web.xml中添加Shiro過濾器("/*" 你的每一次請求都會經(jīng)過過濾器) --- 直接拷貝即可【固定寫法】
- 他是沒有任何功能的,他是一個代理。(他不做工作,把工作委托給別人去做)
注:名字必須和真實過濾器的名字一樣
shiro-root-1.4.0-RC2\samples\spring\src\main\webapp\WEB-INF\web.xml
<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.3 新建application-shiro.xml【在Shiro官方文檔中有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-3.0.xsd">
<!--
配置權(quán)限的核心管理器
-->
<!--拿到SecurityManager對象 DefaultSecurityManager-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--把Realm放到SecurityManager對象中去-->
<property name="realm" ref="jdbcRealm"/>
</bean>
<!--
這里要放一個自定義Realm
-->
<!--class 就是你的自定義Realm的完全限定名-->
<bean id="jdbcRealm" class="cn.zx.aisell.web.shiro.AisellShiro">
<property name="name" value="jdbcRealm"/>
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!--選擇匹配器的類型【MD5】-->
<property name="hashAlgorithmName" value="MD5"/>
<!--選擇匹配器的迭代次數(shù)【10次】-->
<property name="hashIterations" value="10"/>
</bean>
</property>
</bean>
<!--
這個暫時不用但是還是要留著 以防以后看到要用到就可以回來看這個筆記了
這三個bean,就是支持權(quán)限注解判斷的。 我們這里不用注解的形式, 如果要用的話要加這個配置
-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.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>
<bean id="secureRemoteInvocationExecutor" class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!--
shiro真實的過濾器,就是通過這個來完成攔截功能
注:這里的id一定要和web.xml里面的代理過濾器id一樣 不然的話就完成不了攔截功能
-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!--如果沒有登錄,就會調(diào)到這個路徑(一般放登錄頁面)login[登錄]-->
<property name="loginUrl" value="/WEB-INF/views/login"/>
<!--登錄成功進入的路徑(一般放主頁面)-->
<property name="successUrl" value="/WEB-INF/views/index"/>
<!--沒有權(quán)限的時候進入的頁面(一般專門寫一個提示沒有權(quán)限的頁面)-->
<property name="unauthorizedUrl" value="/WEB-INF/views/unauthorized"/>
<!--
路徑 = anon : 不需要登錄也可以訪問的路徑
路徑 = perms[user:*] :你必需要有相應的權(quán)限才能訪問對應的路徑
/** = authc : 需要登錄才能訪問
-->
<!--這個順序是一定要注意的!順序不對是會出問題的。按上面注解排序-->
<property name="filterChainDefinitions">
<value>
/favicon.ico = anon
/logo.png = anon
/shiro.css = anon
/s/login = anon
/*.jar = anon
/** = authc
</value>
</property>
</bean>
</beans>
2.3.1 在application.xml中引入Spring集成shiro的xml
<!--引入shiro.xml【Spring集成Shiro的xml】-->
<import resource="classpath:applicationContext-shiro.xml"/>
2.4 自定義Realm(他是抽象的 要繼承Aut 必須要實現(xiàn)兩個功能(授權(quán)/認證))
/**
* 自定義Realm --- 獲取數(shù)據(jù)
*/
public class AisellShiro extends AuthorizingRealm{//繼承這個類會會實現(xiàn)兩個方法,一個授權(quán)/一個登陸驗證
//授權(quán)功能
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//1.拿到主體(登錄的時候傳過來
String username = (String) principalCollection.getPrimaryPrincipal();
//2.根據(jù)主體拿到數(shù)據(jù)庫的角色和權(quán)限 。(現(xiàn)在在下面拿 因為源碼是要返回Set)
Set<String> roles = this.getRoles(username);
Set<String> perms = this.getPerms(username);
//3.創(chuàng)建AuthenticationInfo對象(把角色和權(quán)限都放進去)
//這里注意:一定要選SimpleAuthorizationInfo。。。差點就選錯了選成SimpleAuthenticationInfo
//一些快了 就不注意了 要小心
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(roles); // 角色
authorizationInfo.setStringPermissions(perms); //權(quán)限
return authorizationInfo;
}
//這里提供給上面角色和權(quán)限的數(shù)據(jù)。 以后是沒用的
public Set<String> getRoles(String username){//roles角色
Set<String> roles = new HashSet<String>();
roles.add("admin");
roles.add("nimda");
return roles;
}
//權(quán)限
public Set<String> getPerms(String username){
Set<String> perms = new HashSet<>();
//perms.add("*");//這就是代表所有權(quán)限
perms.add("employee:*");//這就代表員工里面的所有權(quán)限
return perms;
}
//登陸認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//1.要強轉(zhuǎn)用戶名密碼令牌
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//2.拿到用戶名
String username = token.getUsername();
//3.通過用戶名獲得密碼
String password = this.login(username);
//4.如果密碼為空,就代表用戶不存在 ; 返回空
if (password == null){
return null;
}
//5.準備鹽值
ByteSource salt = ByteSource.Util.bytes("zx");//salt --- 鹽
//6.SimpleAuthenticationInfo是Shiro準備的一個對象(當前用戶名稱,密碼,realm的名稱)
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,salt,getName());
return authenticationInfo;
}
/**
* 因為學習是有方法一步一步來的 所以目前這里目前咋先吧數(shù)據(jù)寫死,后續(xù)會從數(shù)據(jù)庫提取
* 123+迭代10次+鹽值(itsource):d5a3fedf6c59c2ecbe7f7a6c1a22da37
*
* 這里先將上面的數(shù)據(jù)密碼傳到上面,使他們能查到密碼
*/
public String login(String username) {
if ("admin".equals(username)){
return "d5a3fedf6c59c2ecbe7f7a6c1a22da37";
}else if ("xu".equals(username)){
return "123";
}
return null;
}
}
2.4.1 自定義完Realm之后把完全限定名拷貝到application-shiro.xml中
上面代碼已經(jīng)把我自己的自定義Realm的完全限定名搞上去了。以后大家注意!
2.5 .測試
3. 登錄頁面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>夢幻登錄</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
list-style: none;
}
body {
overflow: hidden;
}
#bg_wrap {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
overflow: hidden;
}
#bg_wrap div {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
opacity: 0;
/* 設置透明度 */
transition: opacity 3s;
}
/* nth-of-type(1) *篩選選擇器選擇第一個*/
#bg_wrap div:nth-of-type(1) {
opacity: 1;
}
#Login {
width: 272px;
height: 300px;
margin: 200px auto;
}
#Login .move {
position: absolute;
top: -100px;
z-index: 999;
}
#Login h3 {
width: 270px;
font-size: 30px;
font-weight: 700;
color: #fff;
font-family: '微軟雅黑';
text-align: center;
margin-bottom: 30px;
cursor: move;
}
#Login input.text {
width: 270px;
height: 42px;
color: #fff;
background: rgba(45, 45, 45, 0.15);
border-radius: 6px;
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 1.0) inset;
text-indent: 10px;
}
#Login input.btn {
/* top: 280px; */
background: #ef4300;
width: 272px;
height: 44px;
border-radius: 6px;
color: #fff;
box-shadow: 0 15px 30px 0 rgba(255, 255, 255, 0.25) inset, 0 2px 7px 0 rgba(0, 0, 0, 0.2);
border: 0;
text-align: center;
}
input::-webkit-input-placeholder {
color: #fff;
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
</style>
<%@ include file="/WEB-INF/views/head.jsp"%>
<script>
// 檢查自己是否是頂級頁面 /login
if (top != window) {// 如果不是頂級
//把子頁面的地址,賦值給頂級頁面顯示
window.top.location.href = window.location.href;
}
//var url = "/login";
//就是點擊登錄后成功跳轉(zhuǎn)的頁面
function submitForm(){
// alert("ss");
// loginForm 登錄表單
$('#loginForm').form('submit', {
url:"/login",
//url:url,
onSubmit: function(){
return $(this).form('validate');
},
success:function(data){
//把一個json字符串轉(zhuǎn)成一個json對象
var result = JSON.parse(data);
//登錄成功跳到主頁面,登錄失敗給出提示
if(result.success){
//js的跳轉(zhuǎn)(BOM:瀏覽器對象模型)
window.location.href = "/main";
}else{
$.messager.alert('警告',result.msg,"info");
}
}
});
}
//回車登錄
$(document.documentElement).on("keyup", function(event) {
//console.debug(event.keyCode);
var keyCode = event.keyCode;
console.debug(keyCode);
if (keyCode === 13) { // 捕獲回車
submitForm(); // 提交表單
}
});
</script>
</head>
<body>
<%--圖片--%>
<div id="bg_wrap">
<div><img src="/images/head/1.jpg" width="100%" height="100%"></div>
<div><img src="/images/head/2.jpg" width="100%" height="100%"></div>
<div><img src="/images/head/3.jpg" width="100%" height="100%"></div>
<div><img src="/images/head/4.jpg" width="100%" height="100%"></div>
<div><img src="/images/head/5.jpg" width="100%" height="100%"></div>
<div><img src="/images/head/6.jpg" width="100%" height="100%"></div>
<div><img src="/images/head/8.jpg" width="100%" height="100%"></div>
<div><img src="/images/head/9.jpg" width="100%" height="100%"></div>
<div><img src="/images/head/10.jpg" width="100%" height="100%"></div>
<div><img src="/images/head/11.jpg" width="100%" height="100%"></div>
<div><img src="/images/head/12.jpg" width="100%" height="100%"></div>
<div><img src="/images/head/13.jpg" width="100%" height="100%"></div>
</div>
<div id="Login">
<h3 id="title" class="move">來啦 小老弟~~</h3>
<form id="loginForm" method="post" action="/#">
<input type="text" placeholder="賬號" class="text move" name="username" id="username">
<input type="password" placeholder="密碼" class="text move" name="password" id="password">
<input type="button" value="走 妳" class="btn move" onclick="submitForm()">
</form>
</div>
<script type="text/javascript">
/*背景漸變*/
/*function(){} 匿名函數(shù)
()() IIFE匿名函數(shù)立刻執(zhí)行,函數(shù)自執(zhí)行體*/
//主要是圖片變淺等
(function() {
var timer = null; //聲明定時器
var oImg = document.querySelectorAll('#bg_wrap div') //h5最新元素獲取寫法獲取到的是一組元素
//querySelector獲取單個元素的 兼容ie8
var len = oImg.length; //3
var index = 0;
timer = setInterval(function() {
oImg[index].style.opacity = 0;
index++;
// if(index>=3){
// index=0;
// }
index %= len; //index=index%len求模取余 0%3=0; 1%3=1; 2%3=2; 3%3=0;
oImg[index].style.opacity = 1;
}, 3000);
})();
// 重力模擬彈跳系統(tǒng)
(function() {
/*
改變定位元素的top值
達到指定位置之后進行彈跳一次
多個元素一次運動
動畫序列*/
var oMove = document.querySelectorAll('.move');
var oLen = oMove.length;
var timer = null;
var timeout = null;
var speed = 3; //移動距離
move(oLen - 1);
function move(index) {
if (index < 0) {
clearInterval(timer); //清除循環(huán)定時器
clearTimeout(timeout); //清除延時定時器
return; //終止函數(shù)
}
var endTop = 150 + (index * 60); //根據(jù)下標計算endTop值
timer = setInterval(function() {
speed += 3;
var T = oMove[index].offsetTop + speed; //設置每一次的top值
if (T > endTop) {
T = endTop;
speed *= -1 //取反,讓移動距離變?yōu)樨摂?shù)
speed *= 0.4;
//慢慢停下來
}
oMove[index].style.top = T + 'px';
}, 20);
timeout = setTimeout(function() {
clearInterval(timer);
index--;
console.log(9);
move(index);
console.log(index);
}, 900) //過900毫秒之后再執(zhí)行方法里的代碼
}
})()
</script>
</body>
</html>
3.1 在Controller中創(chuàng)建LoginController
package cn.zx.aisell.web.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController extends BaseController{
@RequestMapping("/login")
public String login(String username,String password){
//拿到當前用戶
Subject subject = SecurityUtils.getSubject();
//如果沒有登錄讓他登錄
//獲得令牌 ---- 判斷
//④.如果沒有登錄,讓他登錄(需要令牌)
if(!subject.isAuthenticated()){
try {
//用戶名密碼令牌
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//登錄功能
subject.login(token);
} catch (UnknownAccountException e) {//Unknown(未知)Account(賬號)Exception
//就是用戶名錯誤
e.printStackTrace();
System.out.println("賬號或者密碼出現(xiàn)錯誤");
} catch (IncorrectCredentialsException e) {//Incorrect(不正確的)Credentials(憑證;證書)Exception
//就是密碼錯誤
e.printStackTrace();
System.out.println("賬號或者密碼出現(xiàn)錯誤!");
}catch (AuthenticationException e){
//就是其他錯誤
e.printStackTrace();
System.out.println("出現(xiàn)一個神迷的錯誤?。。?!");
}
}
//重定向
return "redirect:/main";
}
}
3.2 登錄方法要放行 在【PermissionMapFactory】
permissionMap.put("/login","anon" );
permissionMap.put("/login.jsp","anon" );
//在這里把你所有需要放行放進去
permissionMap.put("*.js","anon");
permissionMap.put("*.css","anon");
permissionMap.put("/css/**","anon");
permissionMap.put("/js/**","anon");
permissionMap.put("/easyui/**","anon");
permissionMap.put("/images/**","anon");
applicationController-shiro.xml
3.3 測試
4.權(quán)限
- 目前是通過登錄頁面進來之后,是沒有權(quán)限的,只要是登錄進來,是可以為所欲為的。還沒有達到需求(只能看員工或者這能看部門等)
4.1這個權(quán)限也是在applicationController-shiro.xml中
4.2.把攔截權(quán)限配置從xml中移到后臺去
- 我們現(xiàn)在是寫死在xml的肯定是不行的,項目中那么多,怎么寫。以后肯定是要從數(shù)據(jù)庫獲取的。上面目前都是假數(shù)據(jù)。所以最好在java代碼中設置這種描述。
4.2.1創(chuàng)建Map的工廠:PermissionMapFactory
注:里面的順序 是不可以亂改的,是有先后循序的
public class PermissionMapFactory {
//搞一個方法 返回Map
private Map<String,String> createPermissions(){
//因為這里我們權(quán)限是有順序的,無序會出錯的,所以我們用LinkedHashMap;
Map<String,String> PermissionMap = new LinkedHashMap<>();
//anon ,不要登錄就能訪問
PermissionMap.put("/s","anon" );
//需要對應的權(quán)限才可以訪問
PermissionMap.put("/WEB-INF/views/department.jsp","perms[department:index]" );
//其他攔截 【攔截所有-- /**】都需要登錄才可訪問
PermissionMap.put("/**","authc");
return PermissionMap;
}
}
4.2.2 然后在applicationController-shiro.xml修改一下
- 用工廠創(chuàng)建bean,在用這個bean創(chuàng)建
<!--
shiro真實的過濾器,就是通過這個來完成攔截功能
注:這里的id一定要和web.xml里面的代理過濾器id一樣 不然的話就完成不了攔截功能
-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!--這里需要Map-->
<property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
</bean>
<!--配置一個bean,它是從工廠的方法中返回的對象-->
<bean id="filterChainDefinitionMap" factory-bean="permissionMapFactory" factory-method="createPermissions"/>
<!--配置我們剛剛寫的工廠【PermissionMapFactory】 -->
<bean id="permissionMapFactory" class="cn.zx.aisell.web.shiro.PermissionMapFactory"/>
注:修改之后記得重啟!?。?/p>