智能銷售系統(tǒng)(五)Shiro權(quán)限框架

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>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容