智能销售系统(五)Shiro权限框架

1.基础介绍以及demo演示网址:

2.Spring集成Shiro【大部分情况下我们使用Shiro都会和Spring集成的】

  • 把Shiro中的重要对象交给Spring管理

2.1导包(之前文档中已经导过)

        <!-- shiro的支持包 (权限管理)-->
        <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过滤器("/*" 你的每一次请求都会经过过滤器) --- 直接拷贝即可【固定写法】

  • 他是没有任何功能的,他是一个代理。(他不做工作,把工作委托给别人去做)

注:名字必须和真实过滤器的名字一样

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">

    <!--
            配置权限的核心管理器
    -->
    <!--拿到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"/>
                <!--选择匹配器的迭代次数【10次】-->
                <property name="hashIterations" value="10"/>
            </bean>
        </property>
    </bean>

    <!--
            这个暂时不用但是还是要留着 以防以后看到要用到就可以回来看这个笔记了
        这三个bean,就是支持权限注解判断的。  我们这里不用注解的形式, 如果要用的话要加这个配置
    -->
    <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"/>
        <!--如果没有登录,就会调到这个路径(一般放登录页面)login[登录]-->
        <property name="loginUrl" value="/WEB-INF/views/login"/>
        <!--登录成功进入的路径(一般放主页面)-->
        <property name="successUrl" value="/WEB-INF/views/index"/>
        <!--没有权限的时候进入的页面(一般专门写一个提示没有权限的页面)-->
        <property name="unauthorizedUrl" value="/WEB-INF/views/unauthorized"/>
        <!--
            路径 = anon : 不需要登录也可以访问的路径              
            路径 = perms[user:*] :你必需要有相应的权限才能访问对应的路径               
            /** = 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 必须要实现两个功能(授权/认证))

/**
 * 自定义Realm ---  获取数据
 */

public class AisellShiro extends AuthorizingRealm{//继承这个类会会实现两个方法,一个授权/一个登陆验证

    //授权功能
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        //1.拿到主体(登录的时候传过来
        String username = (String) principalCollection.getPrimaryPrincipal();

        //2.根据主体拿到数据库的角色和权限  。(现在在下面拿  因为源码是要返回Set)
        Set<String> roles = this.getRoles(username);
        Set<String> perms = this.getPerms(username);

        //3.创建AuthenticationInfo对象(把角色和权限都放进去)
        //这里注意:一定要选SimpleAuthorizationInfo。。。差点就选错了选成SimpleAuthenticationInfo
        //一些快了  就不注意了   要小心
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(roles);  //  角色
        authorizationInfo.setStringPermissions(perms);  //权限
        return authorizationInfo;
    }

    //这里提供给上面角色和权限的数据。  以后是没用的
    public Set<String> getRoles(String username){//roles角色
        Set<String> roles = new HashSet<String>();
        roles.add("admin");
        roles.add("nimda");
        return roles;
    }
    //权限
    public Set<String> getPerms(String username){
        Set<String> perms = new HashSet<>();
        //perms.add("*");//这就是代表所有权限
        perms.add("employee:*");//这就代表员工里面的所有权限
        return perms;
    }


    //登陆认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //1.要强转用户名密码令牌
        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;
    }

    /**
     *      因为学习是有方法一步一步来的  所以目前这里目前咋先吧数据写死,后续会从数据库提取
     *    123+迭代10次+盐值(itsource):d5a3fedf6c59c2ecbe7f7a6c1a22da37 
     *
     *    这里先将上面的数据密码传到上面,使他们能查到密码
     */

    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中

上面代码已经把我自己的自定义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";
        //就是点击登录后成功跳转的页面
        function submitForm(){
           // alert("ss");
           //  loginForm 登录表单
            $('#loginForm').form('submit', {
                url:"/login",
                //url:url,
                onSubmit: function(){
                    return  $(this).form('validate');
                },
                success:function(data){
                    //把一个json字符串转成一个json对象
                    var result = JSON.parse(data);
                    //登录成功跳到主页面,登录失败给出提示
                    if(result.success){
                        //js的跳转(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(){} 匿名函数
     ()()   IIFE匿名函数立刻执行,函数自执行体*/
    //主要是图片变浅等
    (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);
    })();

    // 重力模拟弹跳系统
    (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); //清除循环定时器
                clearTimeout(timeout); //清除延时定时器
                return; //终止函数
            }
            var endTop = 150 + (index * 60); //根据下标计算endTop值
            timer = setInterval(function() {
                speed += 3;
                var T = oMove[index].offsetTop + speed; //设置每一次的top值
                if (T > endTop) {
                    T = endTop;
                    speed *= -1 //取反,让移动距离变为负数
                    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毫秒之后再执行方法里的代码
        }
    })()
</script>
</body>
</html>

3.1 在Controller中创建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("账号或者密码出现错误");

            } catch (IncorrectCredentialsException e) {//Incorrect(不正确的)Credentials(凭证;证书)Exception
                //就是密码错误
                e.printStackTrace();
                System.out.println("账号或者密码出现错误!");
            }catch (AuthenticationException e){
                //就是其他错误
                e.printStackTrace();
                System.out.println("出现一个神迷的错误!!!!");
            }
        }
    //重定向
    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.权限

  • 目前是通过登录页面进来之后,是没有权限的,只要是登录进来,是可以为所欲为的。还没有达到需求(只能看员工或者这能看部门等)

4.1这个权限也是在applicationController-shiro.xml中

4.2.把拦截权限配置从xml中移到后台去

  • 我们现在是写死在xml的肯定是不行的,项目中那么多,怎么写。以后肯定是要从数据库获取的。上面目前都是假数据。所以最好在java代码中设置这种描述。

4.2.1创建Map的工厂:PermissionMapFactory

注:里面的顺序 是不可以乱改的,是有先后循序的

public class PermissionMapFactory {

    //搞一个方法  返回Map
    private Map<String,String> createPermissions(){

        //因为这里我们权限是有顺序的,无序会出错的,所以我们用LinkedHashMap;
        Map<String,String> PermissionMap = new LinkedHashMap<>();

        //anon ,不要登录就能访问
        PermissionMap.put("/s","anon" );

        //需要对应的权限才可以访问
        PermissionMap.put("/WEB-INF/views/department.jsp","perms[department:index]" );

        //其他拦截 【拦截所有-- /**】都需要登录才可访问
        PermissionMap.put("/**","authc");

        return PermissionMap;
    }
}

4.2.2 然后在applicationController-shiro.xml修改一下

  • 用工厂创建bean,在用这个bean创建
    <!--
            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"/>

注:修改之后记得重启!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容