• Shiro使用


    Shiro概述

    Apache Shiro 是Java 的一个安全框架。Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境,也可以用在JavaEE 环境。Shiro 可以帮助我们完成:认证授权加密会话管理与Web 集成缓存等。

    基本功能

    image

    1. Authentication 【ɔː,θentɪ'keɪʃən/】

    身份认证/登录,验证用户是不是拥有相应的身份; 身份 凭证

    2. Authorization【/ɔːθəraɪ'zeɪʃ(ə)n/】

    授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

    3. Session Manager

    会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

    4. Cryptography【/krɪp'tɒgrəfɪ/】

    加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

    5. Web Support

    Web 支持,可以非常容易的集成到Web 环境;

    6. Caching

    缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;

    7. Concurrency【/kən'kɚrənsi】

    shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能 把权限自动传播过去;

    8. Testing

    提供测试支持;

    9. Run As

    允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

    10. Remember Me

    记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

    注意:Shiro不会去维护用户、维护权限,这些需要我们自己去设计/提供,然后通过相应的接口注入给Shiro即可。

    架构说明

    image

    1. Subject

    Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权

    2. SecurityManager【/sɪ'kjʊərətɪ/】

    SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。

    SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。

    3. Authenticator【/ɔ'θɛntɪ,ketɚ/】

    Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。

    4. Authorizer

    Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

    5. realm

    Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。

    注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。

    6. sessionManager

    sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

    7. SessionDAO

    SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。

    8. CacheManager

    CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。

    9. Cryptography【/krɪp'tɒgrəfɪ/】

    Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。

    shiro的下载

    下载地址:http://shiro.apache.org/

    shiro包介绍

    shiro-all   所有shiro的包全导

    shiro-core   核心包

    shiro-web   集合WEB环境需要的包

    shiro-ehcache   缓存处理的包

    shiro-quartz   定时任务

    shiro-spring   集成spring的包

    shiro-spring-boot-starter   集成springboot的java项目的包

    shiro-spring-boot-web-starter   集成springboot的web项目的包

    shiro.ini 文件

    Shiro.ini 文件的说明

    1. ini (InitializationFile) 初始文件.Window系统文件扩展名

    2. Shiro 使用时可以连接数据库,也可以不连接数据库

    如果不连接数据库,可以在shiro.ini中配置静态数据

    1. Shiro的全局配置文件就是.ini文件,ini中数据都是固定数据,后面会用数据库中数据替代下面users和roles(固定数据部分)

    2. .ini文件内容的语法和.properties类似都是key=value,value格式.

    Shrio.ini 文件的组成部分

    INI文件中包含了四个部分:

    1. [main]:定义全局变量

    1. 内置securityManager对象.

    2. 操作内置对象时,在[main]里面写东西

    [main]
    securityManager.属性=值
    myobj=com.bjsxt.lei
    securityManager.对象属性=com.shiro.domain.People		#后面的值是字符串
    
    peo=com.shiro.domain.People
    securityManager.对象属性=$peo        #出现$时才表示是引用对象
    

    2. [users]:定义用户名和密码

    [users]
    # 定义用户名为zhangsan 密码为zs
    zhangsan=zs
    # 定义用户名lisi密码为lisi同时具有role1和role2两个角色
    lisi=lisi,role1,role2
    

    3. [roles]:定义角色具有的权限

    [roles]
    #角色名=权限名1,权限名2
    role1=权限名1,权限名2 
    role2=权限3,权限4
    

    4. [urls]:定义哪些内置urls生效

    定义哪个控制器被哪个过滤器过滤.Shiro内置很多过滤器。此部分主要在WEB应用中使用。

    [urls]
    #url地址 = 内置filter或自定义filter
    #访问时出现/login的url必须去认证.支持authc对应的Filter 
    /login = authc
    #任意的url都不需要进行认证等功能.
    /** = anon
    #所有的内容都必须保证用户已经登录.
    /** = user
    #url abc 访问时必须保证用户具有role1和role2角色.
    /abc = roles[“role1,role2”]
    

    Shiro内置Filter(过滤器)及名称:

    过滤器名称 过滤器类 描述
    anon org.apache.shiro.web.filter.authc.AnonymousFilter 匿名过滤器
    authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter 如果继续操作,需要做对应的表单验证否则不能通过
    authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter 基本http验证过滤,如果不通过,跳转屋登录页面
    logout org.apache.shiro.web.filter.authc.LogoutFilter 登录退出过滤器
    noSessionCreation org.apache.shiro.web.filter.session.NoSessionCreationFilter 没有session创建过滤器
    perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter 权限过滤器
    port org.apache.shiro.web.filter.authz.PortFilter 端口过滤器,可以设置是否是指定端口如果不是跳转到登录页面
    rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter http方法过滤器,可以指定如post不能进行访问等
    roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter 角色过滤器,判断当前用户是否指定角色
    ssl org.apache.shiro.web.filter.authz.SslFilter 请求需要通过ssl,如果不是跳转回登录页
    user org.apache.shiro.web.filter.authc.UserFilter 如果访问一个已知用户,比如记住我功能,走这个过滤器

    anon:某一个路径是否需要认证之后才能访问

    /login/doLogin*=anon   #代表如果用户请求地址为/login/doLogin*  就不用登陆就可以访问
                                 |--http://127.0.0.1:8080/shiro/login/doLogin.action
    
    /**=anon   #代表如果所有地址者不用登陆就可以访问
                                 |--http://127.0.0.1:8080/shiro/aaa.action
    
    /**=authc  #所有路径都人认证之后才能访问
                                 |--http://127.0.0.1:8080/bjsxt/aaa.action
    

    Shiro实现认证【使用shiro.ini】

    基本概念

    1. 身份验证

    即在应用中谁能证明他就是他本人。一般提供如他们的身份ID 一些标识信息来,表明他就是他本人,如提供身份证,用户名/密码来证明。

    在 shiro 中,用户需要提供principals (身份)和credentials(证明)给shiro,从而应用能验证用户身份。

    2. principals【/'prɪnsəpl】

    身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。

    一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。

    3. credentials【/krə'dɛnʃlz/】

    证明/凭证,即只有主体知道的安全值,如密码/数字证书等。

    最常见的principals和credentials组合就是用户名/密码了。接下来先进行一个基本的身份认证

    认证流程

    获取主体,通过主体Subject对象的login方法进行登录;

    把Subject中内容传递给Security Manager;

    Security Manager内部组件Authenticator进行认证;

    认证数据使用InI Realm,调用Ini文件中数据。

    image

    其它就是使用Shrio的认证来取代我们传统的登陆方式。

    名词解释

    Principal:身份。用户名,邮箱,手机等能够唯一确认身份的信息.

    Credential:凭证,代表密码等。

    AuthenticationInfo:认证时存储认证信息。

    创建Maven项目

    Shiro是不依赖于容器的,所以建立一个普通的Maven项目就可以。

    image

    image

    在main目录下创建resources的资源配置文件目录

    在resources目录下面创建shiro.ini

    image

    修改pom.xml引入shiro的依赖

    <!--加入shiro的依赖-->
    <dependency>
      <groupid>org.apache.shiro</groupid>
      <artifactid>shiro-core</artifactid>
      <version>1.5.0</version>
    </dependency>
    <!--日志包-->
    <dependency>
      <groupid>commons-logging</groupid>
      <artifactid>commons-logging</artifactid>
      <version>1.2</version>
    </dependency>
    

    创建log4j.properties

    log4j.rootLogger=debug, stdout
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
    

    创建代码测试

    https://github.com/apache/shiro/blob/master/samples/quickstart/src/main/java/Quickstart.java

    package com.sxt;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.UnknownAccountException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    
    
    /**
     * Hello world!
     */
    public class App {
        public static void main(String[] args) {
            String username = "zhangsan";
            String password = "123456";
            // 1.创建securityManager工厂 SecurityManager JDK也有这个类,在java.lang包 注意不要使用jdk里面那个类
            Factory<securitymanager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            //从工厂里面得到一个 serurityManager
            SecurityManager securityManager = factory.getInstance();
            //把当前的安全管理器绑定到当前线程
            SecurityUtils.setSecurityManager(securityManager);
            //得到当前的Object
            Subject subject = SecurityUtils.getSubject();
            //封装用户名和密码到token
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            //进行登陆
            try {
                subject.login(token);//进行认证,如果失败会抛异常
                System.out.println("登陆成功");
    //        }catch (IncorrectCredentialsException e){
    //            System.out.println("密码不正确");
    //        }catch (UnknownAccountException e){
    //            System.out.println("用户名不存在");
            } catch (AuthenticationException e) {
                System.out.println("用户名或密码正确");
            }
    
            checkUserIsLogin();
    
        }
    
        private static void checkUserIsLogin() {
            Subject subject = SecurityUtils.getSubject();
            subject.isAuthenticated();//判断当前线程里面的Subject是否退出
            System.out.println("是否认证通过:" + subject.isAuthenticated());
            System.out.println("退出");
            subject.logout();//退出
            System.out.println("是否认证通过:" + subject.isAuthenticated());
    
        }
    }
    

    Shiro实现授权【使用shiro.ini】

    1. 授权概述

    授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。

    2. 关键对象介绍

    主体

    主体,即访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访问相应的资源。

    资源

    在应用中用户可以访问的任何东西,比如访问JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。

    权限

    安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权限控制)打印文档等等。

    角色

    角色代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。

    3. 授权流程

    image

    相关方法说明

    subject.hasRole(“”);	//判断是否有角色
    subject.hashRoles(List);	//分别判断用户是否具有List中每个内容
    subject.hasAllRoles(Collection);	//返回boolean,要求参数中所有角色用户都需要具有.
    subject.isPermitted(“”);	//判断是否具有权限
    

    image

    修改pom.xml

    <!--加入shiro的依赖-->
    <dependency>
        <groupid>org.apache.shiro</groupid>
        <artifactid>shiro-core</artifactid>
        <version>1.5.0</version>
    </dependency>
    <!--日志包-->
    <dependency>
        <groupid>commons-logging</groupid>
        <artifactid>commons-logging</artifactid>
        <version>1.2</version>
    </dependency>
    

    配置shiro.ini

    #配置用户名和密码
    [users]
    zhangsan=123456,role1
    lisi=123456,role2,role3
    wangwu=123456,role4
    
    #配置角色
    [roles]
    role1=user:query,user:add,user:update,user:delete,user:exprot
    role2=user:query
    role3=user:query,user:add,user:update,user:delete
    role4=user:query,user:add,user:update
    

    创建代码测试

    package com.sxt;
    
    import com.sun.javafx.font.freetype.HBGlyphLayout;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * Hello world!
     */
    public class App {
        public static void main(String[] args) {
            String username = "zhangsan";
            String password = "123456";
            // 1.创建securityManager工厂 SecurityManager JDK也有这个类,在java.lang包 注意不要使用jdk里面那个类
            Factory<securitymanager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            //从工厂里面得到一个serurityManager
            SecurityManager securityManager = factory.getInstance();
            //把当前的安全管理器绑定到当前线程
            SecurityUtils.setSecurityManager(securityManager);
            //得到当前的Object
            Subject subject = SecurityUtils.getSubject();
            //封装用户名和密码到token
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            //进行登陆
            try {
                subject.login(token);//进行认证,如果失败会抛异常
                System.out.println("登陆成功");
    //        }catch (IncorrectCredentialsException e){
    //            System.out.println("密码不正确");
    //        }catch (UnknownAccountException e){
    //            System.out.println("用户名不存在");
            } catch (AuthenticationException e) {
                System.out.println("用户名或密码正确");
            }
            //判断是否认证通过
            if (subject.isAuthenticated()) {
                //角色相关
                boolean role1 = subject.hasRole("role1");
                System.out.println("判断当前登陆用户是否有role1的角色:" + role1);
                List<string> roles = new ArrayList<>();
                roles.add("role1");
                roles.add("role2");
    //            roles.add("role3");
    //            roles.add("role4");
                boolean[] hasRoles = subject.hasRoles(roles);
                System.out.println("分别判断roles集合里面的角色是否存在返回数组:" + Arrays.toString(hasRoles));
                boolean b = subject.hasAllRoles(roles);
                System.out.println("判断当前登陆用户是否同时拥有roles里面的所有角色:" + b);
    
                //权限相关的
                boolean permitted = subject.isPermitted("user:export");
                System.out.println("判断当前登陆用户是否有user:export的权限" + permitted);
                boolean[] permitted1 = subject.isPermitted("user:query", "user:add", "user:export");
                System.out.println("判断当前登陆用户是否分别拥有某些权限:" + Arrays.toString(permitted1));
                boolean permittedAll = subject.isPermittedAll("user:query", "user:add", "user:export");
                System.out.println("判断当前登陆用户是否同时拥有某些权限:" + permittedAll);
    
            } else {
                System.out.println("您未登陆");
            }
    
        }
    }
    

    加密及凭证匹配器

    在实际开发中数据库中一些敏感信息经常会被加密存储。如:用户密码等。

    Shiro框架内嵌了很多加密算法。如MD5sha1等。使用Shiro框架时可以很方便的实现加密功能。

    package com.sxt.common;
    
    import org.apache.shiro.crypto.hash.Md5Hash;
    import org.apache.shiro.crypto.hash.Sha1Hash;
    
    public class MD5Utils {
    
        public static void main(String[] args) {
            System.out.println(md5("123456", "admin超级管理员", 2));
            System.out.println(md5("123456", "zhangsan张三", 2));
    //        md5("123456","武汉",2);
    //        sha1("123456","武汉",2);
        }
    
        private static String md5(String source, String salt, int hashiteraations) {
            Md5Hash hash1 = new Md5Hash("123456");
            System.out.println("使用MD5加密一次:" + hash1);
            Md5Hash hash2 = new Md5Hash(hash1.toString());
            // 参数说明 source 明文   salt 盐  就是一个加密的混淆字符串
            Md5Hash hash3 = new Md5Hash("123456", "武汉");
            System.out.println("使用MD5加密一次并加盐:" + hash3.toString());
            Md5Hash hash4 = new Md5Hash("123456", "武汉", 3);
            System.out.println("使用SHA1加密3次并加盐:" + hash4.toString());
            Md5Hash hash5 = new Md5Hash(source, salt, hashiteraations);
            return source;
        }
    
        private static void sha1(String source, String salt, int hashiteraations) {
            Sha1Hash hash1 = new Sha1Hash("123456");
            System.out.println("使用SHA1加密一次:" + hash1);
            Sha1Hash hash2 = new Sha1Hash(hash1.toString());
            System.out.println("使用SHA1加密二次:" + hash2);
            //参数说明 source 明文   salt 盐  就是一个加密的混淆字符串
            Sha1Hash hash3 = new Sha1Hash("123456", "武汉");
            System.out.println("使用SHA1加密一次并加盐:" + hash3.toString());
            Sha1Hash hash4 = new Sha1Hash("123456", "武汉", 3);
            System.out.println("使用SHA1加密3次并加盐:" + hash4.toString());
        }
    
    }
    

    thymeleaf中常用属性

    需要在html页面中``添加属性

    `
    
    ### shiro:hasRole="admin"
    
      判断是否具有指定角色。
    
    ### shiro:lacksRole="admin"
    
      判断是否不是没有指定角色。
    
    ### shiro:hasAllRoles="role1,role2"
    
      判断指定角色用户是否都具有。
    
    ### shiro:hasAnyRoles="role1,role2"
    
      只要用户具有其中一个角色就表示判断通过。
    
    ### shiro:hasPermission="userInfo:add"
    
      是否具有指定权限。
    
    ### shiro:lacksPermission="userInfo:del"
    
      是否不具有指定权限
    
    ### shiro:hasAllPermissions="userInfo:view, userInfo:add"
    
      是否全具有指定权限。
    
    ### shiro:hasAnyPermissions="userInfo:view, userInfo:del"
    
      只要有其中任何一个权限即可。
    
    <p style="height:30px;"></p>
    
    # Thymeleaf整合shiro
    
    ### 添加依赖
    
    ```xml
    <dependency>
        <groupid>com.github.theborakompanioni</groupid>
        <artifactid>thymeleaf-extras-shiro</artifactid>
        <version>2.0.0</version>
    </dependency>
    

    修改配置类

    在配置类中com.bjsxt.config.ShiroConfig中添加。

    负责解析thymeleaf中shiro:相关属性。

    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
    

    修改Realm

    绑定用户具有的角色和权限,相关数据应该是从数据库中取出。

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("执行doGetAuthenrization");
        SimpleAuthorizationInfo info= new SimpleAuthorizationInfo();
        info.addRole("role1");
        info.addRole("role2");
        info.addStringPermission("permission1");
        info.addStringPermission("permission2");
    
        return info;
    }
    

    修改index.html

    访问页面后会发现“有角色”不显示,“有权限”显示。

    
    
    
        <meta charset="UTF-8">
        <title>Title</title>
    
    
    index.html
    <a href="/logout">退出</a>
    <span shiro:hasrole="role3">有角色</span>
    <span shiro:haspermission="permission1">有权限</span>
    
    

    使用注解判断方法是否具有权限执行

    方法:可以用控制器方法,也可以是业务方法。常在控制器方法上添加注解进行判断。

    常用注解:

    (1)@RequiresPermissions("") 必须具有指定权限
    (2)@RequiresAuthentication 必须已经认证
    (3)@RequiresRoles("") 必须具有指定角色
    (4)@RequiresUser 必须是已认证或记住用户
    (5)@RequiresGuest 必须是访客

    自定义Realm实现认证

    Shiro默认使用自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm

    Realm接口

    image

    image

    image

    IniRealm的执行流程

    解析shiro.ini

    image

    做认证和授权

    image

    image

    image

    image

    总结

    如果自定义realm只想做认证,继承AuthenticationRealm

    如果自定义realm认证和授权要一起做,继承AuthorizingRealm

    自定义realm做认证

    创建项目

    image

    修改pom.xml

    <!--加入shiro的依赖-->
    <dependency>
        <groupid>org.apache.shiro</groupid>
        <artifactid>shiro-core</artifactid>
        <version>1.5.0</version>
    </dependency>
    <!--日志包-->
    <dependency>
        <groupid>commons-logging</groupid>
        <artifactid>commons-logging</artifactid>
        <version>1.2</version>
    </dependency>
    

    创建User

    package com.sxt.domain;
    
    public class User {
    
        private Integer id;
        private String loginname;
        private String username;
        private String password;
    
        public User() { }
    
        public User(Integer id, String loginname, String username, String password) {
            this.id = id;
            this.loginname = loginname;
            this.username = username;
            this.password = password;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getLoginname() {
            return loginname;
        }
    
        public void setLoginname(String loginname) {
            this.loginname = loginname;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }
    

    创建UserService

    package com.sxt.service;
    
    import com.sxt.domain.User;
    
    public interface UserService {
    
        /**
         * 根据用户名去查询用户对象
         */
        User queryUserByloginName(String loginname);
    
    }
    

    创建UserServiceImpl

    package com.sxt.service.impl;
    
    import com.sxt.domain.User;
    import com.sxt.service.UserService;
    
    public class UserServiceImpl implements UserService {
    
        @Override
        public User queryUserByloginName(String loginname) {
            User user = null;
            switch (loginname) {
                case "zhangsan":
                    user = new User(1, "zhangsan", "张三", "123456");
                    break;
                case "lisi":
                    user = new User(1, "lisi", "李四", "123456");
                    break;
                case "wangwu":
                    user = new User(1, "wangwu", "王五", "123456");
                    break;
            }
            return user;
        }
    
    }
    

    创建UserRealm

    	package com.sxt.realm;
    
    import com.sxt.domain.User;
    import com.sxt.service.UserService;
    import com.sxt.service.impl.UserServiceImpl;
    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.realm.AuthenticatingRealm;
    
    public class UserRealm extends AuthenticatingRealm {
    
        private UserService userService = new UserServiceImpl();
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println(authenticationToken.getPrincipal() + "UserRealm");
            //获取用户名
            String username = authenticationToken.getPrincipal().toString();
            //根据用户名查询用户对象
            User user = userService.queryUserByloginName(username);
            if (null != user){
                System.out.println("用户已存在");
                // 创建这个对象返回之后会自动匹配密码
                AuthenticationInfo info = new SimpleAuthenticationInfo("adc", user.getPassword(), "UserRealm");
                return info;
            }else {
                return null;// 用户不存在
            }
        }
    
    }
    

    修改App.java进行测试

    package com.sxt;
    
    import com.sxt.realm.UserRealm;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    
    /**
     * Hello world!
     */
    public class App
    {
        public static void main( String[] args )
        {
            String username = "zhangsan";
            String password = "123456";
            //创建SecurityManager工厂
            Factory<securitymanager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            //配置自定义realm
            DefaultSecurityManager securityManager = (DefaultSecurityManager)factory.getInstance();
            System.out.println(securityManager.getClass().getSimpleName());
            //配置自定义realm
            securityManager.setRealm(new UserRealm());
            //把当前的安全管理器绑定到当前线程
            SecurityUtils.setSecurityManager(securityManager);
            //得到当前的subject
            Subject subject = SecurityUtils.getSubject();
            //封装用户名和密码到token
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            //进行登录
            try {
                //进行认证,失败则抛异常
                subject.login(token);
            }catch (AuthenticationException e){
                System.out.println("用户名或密码正确");
            }
    
            checkUserIsLogin();
        }
    
        private static void checkUserIsLogin() {
            Subject subject = SecurityUtils.getSubject();
            subject.isAuthenticated();//判断当前线程里面的Subject是否退出
            System.out.println("是否认证通过:"+subject.isAuthenticated());
            System.out.println("退出");
            subject.logout();//退出
            System.out.println("是否认证通过:"+subject.isAuthenticated());
        }
    }
    

    自定义Realm实现授权

    为使用要使用自定义Realm实现授权

    与上边认证自定义realm一样,大部分情况是要从数据库获取权限数据,这里直接实现基于资源的授权。

    创建项目

    image

    修改pom.xml

    <!--加入shiro的依赖-->
    <dependency>
        <groupid>org.apache.shiro</groupid>
        <artifactid>shiro-core</artifactid>
        <version>1.5.0</version>
    </dependency>
    <!--日志包-->
    <dependency>
        <groupid>commons-logging</groupid>
        <artifactid>commons-logging</artifactid>
        <version>1.2</version>
    </dependency>
    

    创建ActiverUser

    package com.sxt.common;
    
    import com.sxt.domain.User;
    
    import java.util.List;
    
    public class ActiveUser {
    
        /**
         * 用户
         */
        private User user;
        /**
         * 角色
         */
        private List<string> roles;
        /**
         * 权限
         */
        private List<string> permissions;
    
        public ActiveUser() { }
    
        public ActiveUser(User user, List<string> roles, List<string> permissions) {
            this.user = user;
            this.roles = roles;
            this.permissions = permissions;
        }
    
        public User getUser() {
            return user;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    
        public List<string> getRoles() {
            return roles;
        }
    
        public void setRoles(List<string> roles) {
            this.roles = roles;
        }
    
        public List<string> getPermissions() {
            return permissions;
        }
    
        public void setPermissions(List<string> permissions) {
            this.permissions = permissions;
        }
    }
    

    创建RoleService

    package com.sxt.service;
    
    import java.util.List;
    
    public interface RoleService {
    
        List<string> queryRolesByLoginName(String loginname);
    }
    

    创建RoleServiceImpl

    package com.sxt.service.impl;
    
    import com.sxt.domain.User;
    import com.sxt.service.RoleService;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    public class RoleServiceImpl implements RoleService {
        @Override
        public List<string> queryRolesByLoginName(String loginname) {
            List<string> roles=new ArrayList<>();
            User user=null;
            switch (loginname){
                case "admin":
                    roles.addAll(Arrays.asList("超级管理员","系统管理员","用户管理员","其它管理员"));
                    break;
                case "zhangsan":
                    roles.addAll(Arrays.asList("系统管理员"));
                    break;
                case "lisi":
                    roles.addAll(Arrays.asList("用户管理员","其它管理员"));
                    break;
                case "wangwu":
                    roles.addAll(Arrays.asList("系统管理员","用户管理员"));
                    break;
            }
            return roles;
        }
    }
    

    创建PermissionService

    package com.sxt.service;
    
    import java.util.List;
    
    public interface PermissionService {
    
        List<string> queryPermissionsByLoginName(String loginname);
    
    }
    

    创建PermissionServiceImpl

    package com.sxt.service.impl;
    
    import com.sxt.domain.User;
    import com.sxt.service.PermissionService;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    public class PermissionServiceImpl implements PermissionService {
        @Override
        public List<string> queryPermissionsByLoginName(String loginname) {
            List<string> roles=new ArrayList<>();
            User user=null;
            switch (loginname){
                case "admin":
                    roles.addAll(Arrays.asList("user:query","user:add","user:update","user:delete","user:export"));
                    break;
                case "zhangsan":
                    roles.addAll(Arrays.asList("user:query","user:add","user:update","user:delete"));
                    break;
                case "lisi":
                    roles.addAll(Arrays.asList("user:query","user:add","user:update","user:delete"));
                    break;
                case "wangwu":
                    roles.addAll(Arrays.asList("user:query"));
                    break;
            }
            return roles;
        }
    }
    

    创建UserRealm

    package com.sxt.realm;
    
    import com.sxt.common.ActiveUser;
    import com.sxt.domain.User;
    import com.sxt.service.PermissionService;
    import com.sxt.service.RoleService;
    import com.sxt.service.UserService;
    import com.sxt.service.impl.PermissionServiceImpl;
    import com.sxt.service.impl.RoleServiceImpl;
    import com.sxt.service.impl.UserServiceImpl;
    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.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    import java.util.List;
    
    /**
     * 用户授权和验证  自定义realm
     */
    public class UserRealm extends AuthorizingRealm {
    
        private UserService userService = new UserServiceImpl();
        private RoleService roleSerivce = new RoleServiceImpl();
        private PermissionService permissionSrvice = new PermissionServiceImpl();
    
        public String getName(){
            return this.getClass().getSimpleName();
        }
    
        /**
         * 授权方法   只要调用 subject.hasRole**   subject.isPermised**就会被回调
         * @param principalCollection
         * @return  返回一个权限和角色的对象
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            //创建返回对象
            SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
            //得到用户对象
            ActiveUser activeUser = (ActiveUser) principalCollection.getPrimaryPrincipal();
            List<string> roles = activeUser.getRoles();
            List<string> permissions = activeUser.getPermissions();
            //把当前用户拥有的角色和权限告诉shiro
            if(null!=roles&&roles.size()>0){
                info.addRoles(roles);
            }
            if(null!=permissions&&permissions.size()>0){
                info.addStringPermissions(permissions);
            }
            return info;
        }
    
        /**
         * 认证方法
         * @param authenticationToken subject.login(toke)方法传过来的token对象
         * @return 如果返回null 代表用户名不存在,返回不为null 说明用户名存在,返回之后在判断用户名是否正确
         * @throws AuthenticationException 认证失败的异常
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println(authenticationToken.getPrincipal() + "UserRealm");
            // 得到用户名
            String username = authenticationToken.getPrincipal().toString();
            // 根据用户登录名查询用户对象
            User user = userService.queryUserByLoginName(username);
            if (user != null){
                // 根据用户登录名取查询当前用户有哪些角色
                List<string> roles = this.roleSerivce.queryRolesByLoginName(user.getLoginname());
                // 根据用户登录名取查询当前用户有哪些权限
                List<string> permissions = this.permissionSrvice.queryPermissionsByLoginName(user.getLoginname());
                ActiveUser activeUser = new ActiveUser(user, roles, permissions);
                /**
                 * 参数说明
                 * principal:向doGetAuthorizationInfo方法和subject.getPrincipal();返回一个对象
                 * credentials:用户密码的密文
                 * realmName:当前自定义Realem类的名称  我们这里是重写getName方法得到
                 */
                AuthenticationInfo info = new SimpleAuthenticationInfo(activeUser, user.getPassword(), this.getName());
                return info;
            }else {
                // 用户不存在
                return null;
            }
        }
    }
    

    创建测试类

    package com.sxt;
    
    import com.sxt.realm.UserRealm;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.UnknownAccountException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    
    /**
     * Hello world!
     */
    public class App {
        public static void main(String[] args) {
            String username = "zhangsan";
            String password = "123456";
            // 创建securityManager工厂 SecurityManager JDK也有这个类,在java.lang包 注意不要使用jdk里面那个类
            Factory<securitymanager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            // 从工厂里面得到一个serurityManager
    //        SecurityManager securityManager = factory.getInstance();
            // 配置自定义realm
            DefaultSecurityManager securityManager = (DefaultSecurityManager) factory.getInstance();
            System.out.println(securityManager.getClass().getSimpleName());
            // 配置自定义realm
            securityManager.setRealm(new UserRealm());
            // 把当前的安全管理器绑定到当前线程
            SecurityUtils.setSecurityManager(securityManager);
            // 得到当前的Object
            Subject subject = SecurityUtils.getSubject();
            // 封装用户名和密码到token
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            // 进行登录
            try {
                // 进行认证,如果失败会抛异常
                subject.login(token);
                System.out.println("登录成功");
                Object principal = subject.getPrincipal();
                System.out.println(principal);
                // 到时候要写到登陆方法里面  可以把principal放到session里面
            } catch (IncorrectCredentialsException e) {
                System.out.println("密码不正确");
            } catch (UnknownAccountException e) {
                System.out.println("用户名不存在");
            } catch (AuthenticationException e) {
                System.out.println("用户名或密码正确");
            }
    
            if (subject.isAuthenticated()) {
                boolean hasRole = subject.hasRole("超级管理员");
                System.out.println(username + "是否有超级管理员的角色:" + hasRole);    //false
                boolean hasRole2 = subject.hasRole("系统管理员");
                System.out.println(username + "是否有系统管理员的角色:" + hasRole2);   //true
                // 权限
                boolean permitted1 = subject.isPermitted("user:query");
                System.out.println(username + "是否有user:query的权限:" + permitted1);    //true
                boolean permitted2 = subject.isPermittedAll("user:query", "user:export");
                System.out.println(username + "是否有同时拥有user:query和user:export的权限:" + permitted2);
            } else {
                System.out.println("未登录");
            }
        }
    }
    

    散列算法 + 凭证配置

    散列算法

    定义:对密码进行加密

    散列算的分类

    Md5

    Sha1

    工具类

    package com.sxt.common;
    
    import org.apache.shiro.crypto.hash.Md5Hash;
    import org.apache.shiro.crypto.hash.Sha1Hash;
    
    public class MD5Utils {
    
        public static void main(String[] args) {
            System.out.println(md5("123456", "admin超级管理员", 2));
            System.out.println(md5("123456", "zhangsan张三", 2));
    //        md5("123456","武汉",2);
    //        sha1("123456","武汉",2);
        }
    
        private static String md5(String source, String salt, int hashiteraations) {
            Md5Hash hash1 = new Md5Hash("123456");
            System.out.println("使用MD5加密一次:" + hash1);
            Md5Hash hash2 = new Md5Hash(hash1.toString());
            // 参数说明 source 明文   salt 盐  就是一个加密的混淆字符串
            Md5Hash hash3 = new Md5Hash("123456", "武汉");
            System.out.println("使用MD5加密一次并加盐:" + hash3.toString());
            Md5Hash hash4 = new Md5Hash("123456", "武汉", 3);
            System.out.println("使用SHA1加密3次并加盐:" + hash4.toString());
            Md5Hash hash5 = new Md5Hash(source, salt, hashiteraations);
            return source;
        }
    
        private static void sha1(String source, String salt, int hashiteraations) {
            Sha1Hash hash1 = new Sha1Hash("123456");
            System.out.println("使用SHA1加密一次:" + hash1);
            Sha1Hash hash2 = new Sha1Hash(hash1.toString());
            System.out.println("使用SHA1加密二次:" + hash2);
            //参数说明 source 明文   salt 盐  就是一个加密的混淆字符串
            Sha1Hash hash3 = new Sha1Hash("123456", "武汉");
            System.out.println("使用SHA1加密一次并加盐:" + hash3.toString());
            Sha1Hash hash4 = new Sha1Hash("123456", "武汉", 3);
            System.out.println("使用SHA1加密3次并加盐:" + hash4.toString());
        }
    
    }
    

    凭证配置

    修改User

    package com.sxt.domain;
    
    public class User {
    
        private Integer id;
        private String loginname;
        private String username;
        private String password;
        private Integer type;   // 0:超级管理员   1:代表普通系统用户
    
        public User() {}
    
        public User(Integer id, String loginname, String username, String password) {
            this.id = id;
            this.loginname = loginname;
            this.username = username;
            this.password = password;
        }
    
        public User(Integer id, String loginname, String username, String password, Integer type) {
            this.id = id;
            this.loginname = loginname;
            this.username = username;
            this.password = password;
            this.type = type;
        }
    
        public Integer getType() {
            return type;
        }
    
        public void setType(Integer type) {
            this.type = type;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getLoginname() {
            return loginname;
        }
    
        public void setLoginname(String loginname) {
            this.loginname = loginname;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }
    

    修改UserServiceImpl

    package com.sxt.service.impl;
    
    import com.sxt.domain.User;
    import com.sxt.service.UserService;
    
    public class UserServiceImpl implements UserService {
        @Override
        public User queryUserByloginName(String loginname) {
            User user=null;
            switch (loginname){
                case "admin":
                    user=new User(1, "admin", "超级管理员", "e0cb1490abfd76c2ffd5a63a531963b6", 0);
                    break;
                case "zhangsan":
                    user=new User(1, "zhangsan", "张三", "94e46f8fe8d948aab8943543e5da99e9", 1);
                    break;
                case "lisi":
                    user=new User(1, "lisi", "李四", "92932a67efcf212077659c69b989c460", 1);
                    break;
                case "wangwu":
                    user=new User(1, "wangwu", "王五", "2399a27720bfb00f20fbce035af38649", 1);
                    break;
            }
            return user;
        }
    }
    

    修改UserRealm

    修改认证方法

    /**
      * 参数说明
      * 参数1:向doGetAuthorizationInfo方法和subject.getPrincipal();返回一个对象
      * 参数2:数据库里面存放的加密的密文
      * 参数3:盐
      * 参数4:当前realm的名字
      */
     AuthenticationInfo info=new SimpleAuthenticationInfo(activeUser,user.getPassword(),credentialsSalt,this.getName());
    

    测试并加入凭证匹配器

    package com.sxt;
    
    import com.sxt.realm.UserRealm;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.UnknownAccountException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    
    /**
     * Hello world!
     */
    public class App {
        public static void main(String[] args) {
            String username = "zhangsan";
            String password = "123456";
            // 创建securityManager工厂 SecurityManager JDK也有这个类,在java.lang包 注意不要使用jdk里面那个类
            Factory<securitymanager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            // 从工厂里面得到一个serurityManager
    //        SecurityManager securityManager = factory.getInstance();
            // 配置自定义realm
            DefaultSecurityManager securityManager = (DefaultSecurityManager) factory.getInstance();
            System.out.println(securityManager.getClass().getSimpleName());
    
            // 配置自定义realm
            UserRealm realm = new UserRealm();
    
            // 设置凭证匹配器
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
            credentialsMatcher.setHashAlgorithmName("md5");//加密方式
            credentialsMatcher.setHashIterations(2); //散列次数
            realm.setCredentialsMatcher(credentialsMatcher);
            securityManager.setRealm(realm);
    
            // 把当前的安全管理器绑定到当前线程
            SecurityUtils.setSecurityManager(securityManager);
            // 得到当前的Object
            Subject subject = SecurityUtils.getSubject();
            // 封装用户名和密码到token
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            // 进行登录
            try {
                // 进行认证,如果失败会抛异常
                subject.login(token);
                System.out.println("登录成功");
                Object principal = subject.getPrincipal();
                System.out.println(principal);
                // 到时候要写到登录方法里面  可以把principal放到session里面
            } catch (IncorrectCredentialsException e) {
                System.out.println("密码不正确");
            } catch (UnknownAccountException e) {
                System.out.println("用户名不存在");
            } catch (AuthenticationException e) {
                System.out.println("用户名或密码正确");
            }
    
            if (subject.isAuthenticated()) {
                boolean hasRole = subject.hasRole("超级管理员");
                System.out.println(username + "是否有超级管理员的角色:" + hasRole);//false
                boolean hasRole2 = subject.hasRole("系统管理员");
                System.out.println(username + "是否有系统管理员的角色:" + hasRole2);//true
                // 权限
                boolean permitted1 = subject.isPermitted("user:query");
                System.out.println(username + "是否有user:query的权限:" + permitted1);//true
                boolean permitted2 = subject.isPermittedAll("user:query", "user:export");
                System.out.println(username + "是否有同时拥有user:query和user:export的权限:" + permitted2);//
            } else {
                System.out.println("未登录");
            }
        }
    }
    

    手动创建SSM集成

    创建项目

    image

    image

    修改pom.xml引入相关包

    Spring、Springmvc、jackson、Mybatis、Pagehelper、Druid、Mysql、Log4j、文件上传

    <!--?xml version="1.0" encoding="UTF-8"?-->
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelversion>4.0.0</modelversion>
    
      <groupid>com.sxt</groupid>
      <artifactid>05ssm</artifactid>
      <version>1.0-SNAPSHOT</version>
      <packaging>war</packaging>
    
      <name>05ssm Maven Webapp</name>
      <url>http://www.example.com</url>
    
      <properties>
        <project.build.sourceencoding>UTF-8</project.build.sourceencoding>
        <servlet.version>3.1.0</servlet.version>
        <jsp.version>2.3.1</jsp.version>
        <spring.version>4.3.24.RELEASE</spring.version>
        <jackson.version>2.10.0</jackson.version>
        <mybatis.version>3.5.4</mybatis.version>
        <mybatis-spring.version>2.0.3</mybatis-spring.version>
        <pagehelper.version>5.1.11</pagehelper.version>
        <mysql.version>8.0.19</mysql.version>
        <druid.version>1.1.21</druid.version>
        <fileupload.version>1.4</fileupload.version>
        <logging.version>1.2</logging.version>
        <log4j.version>1.2.17</log4j.version>
      </properties>
    
      <dependencies>
        <!--servlet -->
        <dependency>
          <groupid>javax.servlet</groupid>
          <artifactid>javax.servlet-api</artifactid>
          <version>${servlet.version}</version>
          <scope>provided</scope>
        </dependency>
        <!-- javax.servlet.jsp -->
        <dependency>
          <groupid>javax.servlet.jsp</groupid>
          <artifactid>javax.servlet.jsp-api</artifactid>
          <version>${jsp.version}</version>
          <scope>provided</scope>
        </dependency>
    
        <!--spring的引入-->
        <dependency>
          <groupid>org.springframework</groupid>
          <artifactid>spring-aop</artifactid>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupid>org.springframework</groupid>
          <artifactid>spring-aspects</artifactid>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupid>org.springframework</groupid>
          <artifactid>spring-context-support</artifactid>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupid>org.springframework</groupid>
          <artifactid>spring-orm</artifactid>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupid>org.springframework</groupid>
          <artifactid>spring-oxm</artifactid>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupid>org.springframework</groupid>
          <artifactid>spring-webmvc</artifactid>
          <version>${spring.version}</version>
        </dependency>
    
        <!-- mybatis -->
        <dependency>
          <groupid>org.mybatis</groupid>
          <artifactid>mybatis</artifactid>
          <version>${mybatis.version}</version>
        </dependency>
    
        <!-- mybatis-spring -->
        <dependency>
          <groupid>org.mybatis</groupid>
          <artifactid>mybatis-spring</artifactid>
          <version>${mybatis-spring.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
        <dependency>
          <groupid>com.github.pagehelper</groupid>
          <artifactid>pagehelper</artifactid>
          <version>${pagehelper.version}</version>
        </dependency>
    
        <!-- mysql-connector-java -->
        <dependency>
          <groupid>mysql</groupid>
          <artifactid>mysql-connector-java</artifactid>
          <version>${mysql.version}</version>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
          <groupid>com.alibaba</groupid>
          <artifactid>druid</artifactid>
          <version>${druid.version}</version>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
        <dependency>
          <groupid>commons-fileupload</groupid>
          <artifactid>commons-fileupload</artifactid>
          <version>${fileupload.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
        <dependency>
          <groupid>commons-logging</groupid>
          <artifactid>commons-logging</artifactid>
          <version>${logging.version}</version>
        </dependency>
    
        <dependency>
          <groupid>log4j</groupid>
          <artifactid>log4j</artifactid>
          <version>${log4j.version}</version>
        </dependency>
      </dependencies>
    
      <build>
        <finalname>05ssm</finalname>
        <plugins>
          <!-- 加入tomcat运行插件 -->
          <plugin>
            <groupid>org.apache.tomcat.maven</groupid>
            <artifactid>tomcat7-maven-plugin</artifactid>
            <version>2.2</version>
            <configuration>
              <!--解决页面提交数据乱码问题 -->
              <uriencoding>UTF-8</uriencoding>
              <!-- tomcat插件的请求端口 -->
              <port>8080</port>
              <!-- 项目的请求路径 -->
              <path>/bjsxt</path>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </project>
    

    创建db.properties

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://127.0.0.1:3306/shiro?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username=root
    password=root
    initialSize=5
    maxActive=10
    minIdle=3
    

    创建log4j.properties

    # Global logging configuration
    log4j.rootLogger=DEBUG, stdout
    # MyBatis logging configuration...
    log4j.logger.org.mybatis.example.BlogMapper=TRACE
    # Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    

    创建appliction-dao.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:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--加载db.properties-->
        <context:property-placeholder location="classpath*:db.properties" system-properties-mode="FALLBACK"></context:property-placeholder>
    
        <!--配置数据源-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init">
            <property name="driverClassName" value="${driverClassName}"></property>
            <property name="url" value="${url}"></property>
            <property name="username" value="${username}"></property>
            <property name="password" value="${password}"></property>
    
            <property name="initialSize" value="${initialSize}"></property>
            <property name="maxActive" value="${maxActive}"></property>
            <property name="minIdle" value="${minIdle}"></property>
        </bean>
    
        <!--声明配置对象-->
        <bean id="configuration" class="org.apache.ibatis.session.Configuration">
            <!--在控制台输出sql-->
            <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"></property>
        </bean>
    
        <!--声明 sqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!--注入数据源-->
            <property name="dataSource" ref="dataSource"></property>
            <!--加入配置文件-->
            <!--<property name="configLocation" value="classpath*:mybatis.cfg.xml"></property>-->
            <property name="configuration" ref="configuration"></property>
            <!--配置mppaer.xml的扫描-->
            <property name="mapperLocations" value="classpath*:mapper/*Mapper.xml"></property>
            <!--配置分页插件-->
            <property name="plugins">
                <bean class="com.github.pagehelper.PageInterceptor"></bean>
            </property>
        </bean>
    
        <!--配置mapper接口的扫描-->
        <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.sxt.mapper"></property>
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
        </bean>
    
    </beans>
    

    创建appliction-service.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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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">
        <!--扫描service.impl-->
        <context:component-scan base-package="com.sxt.service.impl"></context:component-scan>
    
        <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!--配置事务的传播特性-->
        <tx:advice id="advise" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="insert*" propagation="REQUIRED">
                <tx:method name="save*" propagation="REQUIRED">
                <tx:method name="update*" propagation="REQUIRED">
                <tx:method name="delete*" propagation="REQUIRED">
                <tx:method name="del*" propagation="REQUIRED">
                <tx:method name="change*" propagation="REQUIRED">
                <tx:method name="add*" propagation="REQUIRED">
                <tx:method name="get*" read-only="true">
                <tx:method name="load*" read-only="true">
                <tx:method name="query*" read-only="true">
            </tx:method></tx:method></tx:method></tx:method></tx:method></tx:method></tx:method></tx:method></tx:method></tx:method></tx:attributes>
        </tx:advice>
    
        <!--配置切面-->
        <aop:config>
            <!--声明切面-->
            <aop:pointcut id="pc" expression="execution(* com.sxt.service.impl.*.*(..))"></aop:pointcut>
            <!--织入-->
            <aop:advisor advice-ref="advise" pointcut-ref="pc"></aop:advisor>
        </aop:config>
    
    </beans>
    

    创建applictionContext.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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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">
    
           <import resource="classpath*:application-dao.xml"></import>
           <import resource="classpath*:application-service.xml"></import>
    
    </beans>
    

    创建springmvc.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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemalocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
            <!--扫描controller-->
            <context:component-scan base-package="com.sxt.controller"></context:component-scan>
    
            <!--配置映射器和适配器-->
            <mvc:annotation-driven></mvc:annotation-driven>
    
            <!--配置视图解析器的前后缀-->
            <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                <!-- 注入前后缀 -->
                <property name="prefix" value="/WEB-INF/view/"></property>
                <property name="suffix" value=".jsp"></property>
            </bean>
    
            <!--配置文件上传-->
            <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
                <property name="defaultEncoding" value="utf-8"></property>
                <property name="maxUploadSize" value="20971520"></property>
                <property name="uploadTempDir" value="/upload/temp"></property>
            </bean>
    
            <!-- 拦截器 -->
    
            <!-- 配置静态文件放行 -->
            <mvc:default-servlet-handler>
    </mvc:default-servlet-handler></beans>
    
    <!--?xml version="1.0" encoding="UTF-8"?-->
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemalocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
    
      <!--配置编码过滤器开始-->
      <filter>
        <filter-name>encFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
          <param-name>encoding</param-name>
          <param-value>UTF-8</param-value>
        </init-param>
      </filter>
      <filter-mapping>
        <filter-name>encFilter</filter-name>
        <servlet-name>springmvc</servlet-name>
      </filter-mapping>
      <!--配置编码过滤器结束-->
    
      <!--配置加载applicationContext 监听器开始-->
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml</param-value>
      </context-param>
      <!--配置加载applicationContext 监听器结束-->
    
      <!--配置前端控制器开始-->
      <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath*:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
      
      <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.do</url-pattern>
      </servlet-mapping>
      <!--配置编码过滤器结束-->
      
      <!--配置durid的监控页面 开始-->
      <servlet>
        <servlet-name>duridServlet</servlet-name>
        <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
        <init-param>
          <param-name>loginUsername</param-name>
          <param-value>admin</param-value>
        </init-param>
        <init-param>
          <param-name>loginPassword</param-name>
          <param-value>123456</param-value>
        </init-param>
      </servlet>
      <servlet-mapping>
        <servlet-name>duridServlet</servlet-name>
        <url-pattern>/durid/*</url-pattern>
      </servlet-mapping>
      <!--配置durid的监控页面 结束-->
    
    </web-app>
    

    创建web.xml

    相关的包

    image

    时区问题

    image

    监控页面的问题

    image

    image

    SSM+集成shiro+jsp

    创建数据库【shiro】

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    -- Table structure for permission
    -- ----------------------------
    DROP TABLE IF EXISTS `permission`;
    CREATE TABLE `permission`  (
      `perid` int(11) NOT NULL AUTO_INCREMENT,
      `pername` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `percode` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      PRIMARY KEY (`perid`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of permission
    -- ----------------------------
    INSERT INTO `permission` VALUES (1, '用户查询', 'person:query');
    INSERT INTO `permission` VALUES (2, '用户添加', 'person:add');
    INSERT INTO `permission` VALUES (3, '用户修改', 'person:update');
    INSERT INTO `permission` VALUES (4, '用户删除', 'person:delete');
    INSERT INTO `permission` VALUES (5, '导出用户', 'person:export');
    
    -- ----------------------------
    -- Table structure for role
    -- ----------------------------
    DROP TABLE IF EXISTS `role`;
    CREATE TABLE `role`  (
      `roleid` int(11) NOT NULL AUTO_INCREMENT,
      `rolename` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      PRIMARY KEY (`roleid`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of role
    -- ----------------------------
    INSERT INTO `role` VALUES (1, '超级管理员');
    INSERT INTO `role` VALUES (2, 'CEO');
    INSERT INTO `role` VALUES (3, '保安');
    
    -- ----------------------------
    -- Table structure for role_permission
    -- ----------------------------
    DROP TABLE IF EXISTS `role_permission`;
    CREATE TABLE `role_permission`  (
      `perid` int(255) NULL DEFAULT NULL,
      `roleid` int(11) NULL DEFAULT NULL
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of role_permission
    -- ----------------------------
    INSERT INTO `role_permission` VALUES (1, 1);
    INSERT INTO `role_permission` VALUES (2, 1);
    INSERT INTO `role_permission` VALUES (3, 1);
    INSERT INTO `role_permission` VALUES (4, 1);
    INSERT INTO `role_permission` VALUES (1, 2);
    INSERT INTO `role_permission` VALUES (2, 2);
    INSERT INTO `role_permission` VALUES (3, 2);
    INSERT INTO `role_permission` VALUES (1, 3);
    INSERT INTO `role_permission` VALUES (5, 3);
    
    -- ----------------------------
    -- Table structure for user
    -- ----------------------------
    DROP TABLE IF EXISTS `user`;
    CREATE TABLE `user`  (
      `userid` int(11) NOT NULL AUTO_INCREMENT,
      `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `userpwd` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `type` int(11) NULL DEFAULT 1 COMMENT '0:超级管理员	1:系统普通用户',
      PRIMARY KEY (`userid`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of user
    -- ----------------------------
    INSERT INTO `user` VALUES (1, 'zhangsan', '639ffb0cbcca39d4fff8348844b1974e', '男', '武汉', 1);
    INSERT INTO `user` VALUES (2, 'lisi', '0d303fa8e2e2ca98555f23a731a58dd9', '女', '北京', 1);
    INSERT INTO `user` VALUES (3, 'wangwu', '473c41db9af5cc0d90e7adfd2b6d9180', '女', '成都', 1);
    INSERT INTO `user` VALUES (4, 'admin', '65bba883c29e1e006306d2ff4db96b84', ' 男', '武汉', 0);
    
    -- ----------------------------
    -- Table structure for user_role
    -- ----------------------------
    DROP TABLE IF EXISTS `user_role`;
    CREATE TABLE `user_role`  (
      `userid` int(11) NULL DEFAULT NULL,
      `roleid` int(11) NULL DEFAULT NULL
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of user_role
    -- ----------------------------
    INSERT INTO `user_role` VALUES (1, 1);
    INSERT INTO `user_role` VALUES (2, 2);
    INSERT INTO `user_role` VALUES (3, 3);
    
    SET FOREIGN_KEY_CHECKS = 1;
    

    创建项目

    image

    image

    生成User

    image

    生成UserService

    package com.sxt.service;
    
    import com.sxt.domain.User;
    
    public interface UserService{
    
        /**
         * 根据登陆名查询用户对象
         */
        public User queryUserByUserName(String userName);
    
    }
    

    生成UserServiceImpl

    package com.sxt.service.impl;
    
    import com.sxt.domain.User;
    import org.springframework.stereotype.Service;
    import javax.annotation.Resource;
    import com.sxt.mapper.UserMapper;
    import com.sxt.service.UserService;
    
    @Service
    public class UserServiceImpl implements UserService{
    
        @Resource
        private UserMapper userMapper;
    
        @Override
        public User queryUserByUserName(String userName) {
            return this.userMapper.queryUserByUserName(userName);
        }
    
    }
    

    生成UserMapper

    package com.sxt.mapper;
    
    import com.sxt.domain.User;
    
    public interface UserMapper {
    
        User queryUserByUserName(String userName);
    
    }
    

    生成UserMapper.xml

    <select id="queryUserByUserName" resultmap="BaseResultMap">
          select * from user where username = #{value}
    </select>
    

    生成Role

    生成RoleService

    package com.sxt.service;
    
    import java.util.List;
    
    public interface RoleService{
    
        /**
         * 根据用户ID查询当前用户拥有的角色
         * 在realm里面使用的List<string>
         */
        List<string> queryRolesByUserId(Integer userId);
    
    }
    

    生成RoleServiceImpl

    package com.sxt.service.impl;
    
    import com.sxt.domain.Role;
    import org.springframework.stereotype.Service;
    import javax.annotation.Resource;
    import com.sxt.mapper.RoleMapper;
    import com.sxt.service.RoleService;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Service
    public class RoleServiceImpl implements RoleService{
    
        @Resource
        private RoleMapper roleMapper;
    
        @Override
        public List<string> queryRolesByUserId(Integer userId) {
          List<role> rolesList= this.roleMapper.queryRolesByUserId(userId);
          List<string> roles=new ArrayList<>();
            for (Role role : rolesList) {
                roles.add(role.getRolename());
            }
            return roles;
        }
    
    }
    

    生成RoleMapper

    package com.sxt.mapper;
    
    import com.sxt.domain.Role;
    
    import java.util.List;
    
    public interface RoleMapper {
    
        List<role> queryRolesByUserId(Integer userId);
    
    }
    

    生成RoleMapper.xml

    <select id="queryRolesByUserId" resultmap="BaseResultMap">
          select t1.* from role t1 inner join  user_role t2 on (t1.roleid=t2.userid)
          where t2.userid = #{value}
    </select>
    

    生成Permssion

    生成PermssionService

    package com.sxt.service;
    
    import java.util.List;
    
    public interface PermissionService{
    
        /**
         * 根据用户ID查询当前用户拥有的角色
         * 在realm里面使用的List<string>
         */
        List<string> queryPermissionsByUserId(Integer userId);
    
    }
    

    生成PermssionServiceImp

    package com.sxt.service.impl;
    
    import com.sxt.domain.Permission;
    import org.springframework.stereotype.Service;
    import javax.annotation.Resource;
    import com.sxt.mapper.PermissionMapper;
    import com.sxt.service.PermissionService;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Service
    public class PermissionServiceImpl implements PermissionService{
    
        @Resource
        private PermissionMapper permissionMapper;
    
        @Override
        public List<string> queryPermissionsByUserId(Integer userId) {
            List<permission> permissionsList=this.permissionMapper.queryPermissionsByUserId(userId);
            List<string> permissions=new ArrayList<>();
            for (Permission permission : permissionsList) {
                permissions.add(permission.getPercode());
            }
            return permissions ;
        }
    
    }
    

    生成PermssionMapper

    <select id="queryPermissionsByUserId" resultmap="BaseResultMap">
        select distinct t1.* from permission t1 inner join role_permission t2 inner join  user_role t3
        on(t1.perid=t2.perid and t2.roleid=t3.userid)
        where t3.userid=#{value}
    </select>
    

    创建ActiverUser

    package com.sxt.common;
    
    import com.sxt.domain.User;
    
    import java.util.List;
    
    public class ActiveUser {
    
        /**
         * 用户
         */
        private User user;
        /**
         * 角色
         */
        private List<string> roles;
        /**
         * 权限
         */
        private List<string> permissions;
    
        public ActiveUser(User user, List<string> roles, List<string> permissions) {
            this.user = user;
            this.roles = roles;
            this.permissions = permissions;
        }
    
        public User getUser() {
            return user;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    
        public List<string> getRoles() {
            return roles;
        }
    
        public void setRoles(List<string> roles) {
            this.roles = roles;
        }
    
        public List<string> getPermissions() {
            return permissions;
        }
    
        public void setPermissions(List<string> permissions) {
            this.permissions = permissions;
        }
    }
    

    创建UserRealm

    package com.sxt.realm;
    
    import com.sxt.common.ActiveUser;
    import com.sxt.domain.User;
    import com.sxt.service.PermissionService;
    import com.sxt.service.RoleService;
    import com.sxt.service.UserService;
    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.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    public class UserRealm extends AuthorizingRealm {
    
        @Autowired
        private UserService userService;
    
        @Autowired
        private RoleService roleService;
    
        @Autowired
        private PermissionService permissionService;
    
        @Override
        public String getName() {
            return this.getClass().getSimpleName();
        }
    
        /**
         * 认证
         * @param authenticationToken
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            //得到用户名
            String username = authenticationToken.getPrincipal().toString();
            //根据登陆名查询用户对象
            User user = userService.queryUserByUserName(username);
            if(null != user){
                // 根据用户登陆名去查询当前用户有哪些角色
                List<string> roles = roleService.queryRolesByUserId(user.getUserid());
                // 根据用户登陆名去查询当前用户有哪些权限
                List<string> permissions = permissionService.queryPermissionsByUserId(user.getUserid());
                ActiveUser activeUser = new ActiveUser(user, roles, permissions);
                /**
                 * 参数说明
                 * principal:向doGetAuthorizationInfo方法和subject.getPrincipal();返回一个对象
                 * credentials:用户密码的密文
                 * realmName:当前自定义Realem类的名称  我们这里是重写getName方法得到
                 */
    //            AuthenticationInfo info = new SimpleAuthenticationInfo(activeUser, user.getPassword(), this.getName());
    
                ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername() + user.getAddress());	//盐
                /**
                 * 参数说明
                 * 参数1:向doGetAuthorizationInfo方法和subject.getPrincipal();返回一个对象
                 * 参数2:数据库里面存放的加密的密文
                 * 参数3:盐
                 * 参数4:当前realm的名字
                 */
                AuthenticationInfo info = new SimpleAuthenticationInfo(activeUser, user.getUserpwd(), credentialsSalt, this.getName());
                return info;
            }else{
                return null; //用户不存在
            }
        }
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            ActiveUser activeUser = (ActiveUser) principalCollection.getPrimaryPrincipal();
            List<string> roles = activeUser.getRoles();
            User user = activeUser.getUser();
            List<string> permissions = activeUser.getPermissions();
            if(user.getType() == 0){
                // 超级管理员登陆
                System.out.println("超级管理员");
                info.addStringPermission("*:*");
            }else {
                // 把当前用户拥有的角色和权限告诉shiro
                if(null != roles && roles.size() > 0){
                    info.addRoles(roles);
                }
    
                if(null != permissions && permissions.size() > 0){
                    info.addStringPermissions(permissions);
                }
            }
            return info;
        }
    }
    

    修改pom.xml

    引入相关包

    Spring、Springmvc、jackson、Mybatis、Pagehelper、Druid、Mysql、Log4j、文件上传

    <!--?xml version="1.0" encoding="UTF-8"?-->
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelversion>4.0.0</modelversion>
    
        <groupid>com.sxt</groupid>
        <artifactid>05ssm</artifactid>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
        <name>05ssm Maven Webapp</name>
        <url>http://www.example.com</url>
    
        <properties>
            <project.build.sourceencoding>UTF-8</project.build.sourceencoding>
            <servlet.version>3.1.0</servlet.version>
            <jsp.version>2.3.1</jsp.version>
            <spring.version>4.3.24.RELEASE</spring.version>
            <jackson.version>2.10.0</jackson.version>
            <mybatis.version>3.5.4</mybatis.version>
            <mybatis-spring.version>2.0.3</mybatis-spring.version>
            <pagehelper.version>5.1.11</pagehelper.version>
            <mysql.version>8.0.19</mysql.version>
            <druid.version>1.1.21</druid.version>
            <fileupload.version>1.4</fileupload.version>
            <logging.version>1.2</logging.version>
            <log4j.version>1.2.17</log4j.version>
            <shiro.version>1.5.0</shiro.version>
        </properties>
    
        <dependencies>
            <!--servlet -->
            <dependency>
                <groupid>javax.servlet</groupid>
                <artifactid>javax.servlet-api</artifactid>
                <version>${servlet.version}</version>
                <scope>provided</scope>
            </dependency>
            <!-- javax.servlet.jsp -->
            <dependency>
                <groupid>javax.servlet.jsp</groupid>
                <artifactid>javax.servlet.jsp-api</artifactid>
                <version>${jsp.version}</version>
                <scope>provided</scope>
            </dependency>
    
            <!--spring的引入-->
            <dependency>
                <groupid>org.springframework</groupid>
                <artifactid>spring-aop</artifactid>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupid>org.springframework</groupid>
                <artifactid>spring-aspects</artifactid>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupid>org.springframework</groupid>
                <artifactid>spring-context-support</artifactid>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupid>org.springframework</groupid>
                <artifactid>spring-orm</artifactid>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupid>org.springframework</groupid>
                <artifactid>spring-oxm</artifactid>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupid>org.springframework</groupid>
                <artifactid>spring-webmvc</artifactid>
                <version>${spring.version}</version>
            </dependency>
    
            <!-- mybatis -->
            <dependency>
                <groupid>org.mybatis</groupid>
                <artifactid>mybatis</artifactid>
                <version>${mybatis.version}</version>
            </dependency>
    
            <!-- mybatis-spring -->
            <dependency>
                <groupid>org.mybatis</groupid>
                <artifactid>mybatis-spring</artifactid>
                <version>${mybatis-spring.version}</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
            <dependency>
                <groupid>com.github.pagehelper</groupid>
                <artifactid>pagehelper</artifactid>
                <version>${pagehelper.version}</version>
            </dependency>
    
            <!-- mysql-connector-java -->
            <dependency>
                <groupid>mysql</groupid>
                <artifactid>mysql-connector-java</artifactid>
                <version>${mysql.version}</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
            <dependency>
                <groupid>com.alibaba</groupid>
                <artifactid>druid</artifactid>
                <version>${druid.version}</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
            <dependency>
                <groupid>commons-fileupload</groupid>
                <artifactid>commons-fileupload</artifactid>
                <version>${fileupload.version}</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
            <dependency>
                <groupid>commons-logging</groupid>
                <artifactid>commons-logging</artifactid>
                <version>${logging.version}</version>
            </dependency>
    
            <dependency>
                <groupid>log4j</groupid>
                <artifactid>log4j</artifactid>
                <version>${log4j.version}</version>
            </dependency>
    
            <!--引入shiro-->
            <dependency>
                <groupid>org.apache.shiro</groupid>
                <artifactid>shiro-spring</artifactid>
                <version>${shiro.version}</version>
            </dependency>
    
        </dependencies>
    
        <build>
            <finalname>05ssm</finalname>
            <plugins>
                <!-- 加入tomcat运行插件 -->
                <plugin>
                    <groupid>org.apache.tomcat.maven</groupid>
                    <artifactid>tomcat7-maven-plugin</artifactid>
                    <version>2.2</version>
                    <configuration>
                        <!--解决页面提交数据乱码问题 -->
                        <uriencoding>UTF-8</uriencoding>
                        <!-- tomcat插件的请求端口 -->
                        <port>8080</port>
                        <!-- 项目的请求路径 -->
                        <path>/ssm-shiro</path>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    

    创建db.properties

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://127.0.0.1:3306/shiro?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username=root
    password=root
    initialSize=5
    maxActive=10
    minIdle=3
    

    创建log4j.properties

    # Global logging configuration
    log4j.rootLogger=DEBUG, stdout
    # MyBatis logging configuration...
    log4j.logger.org.mybatis.example.BlogMapper=TRACE
    # Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    

    创建appliction-dao.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:context="http://www.springframework.org/schema/context" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--加载db.properties-->
        <context:property-placeholder location="classpath*:db.properties" system-properties-mode="FALLBACK">
    
        <!--配置数据源-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init">
            <property name="driverClassName" value="${driverClassName}">
            <property name="url" value="${url}">
            <property name="username" value="${username}">
            <property name="password" value="${password}">
            <property name="initialSize" value="${initialSize}">
            <property name="maxActive" value="${maxActive}">
            <property name="minIdle" value="${minIdle}">
        </property></property></property></property></property></property></property></bean>
    
        <!--声明配置对象-->
        <bean id="configuration" class="org.apache.ibatis.session.Configuration">
            <!--在控制台输出sql-->
            <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl">
        </property></bean>
    
        <!--声明 sqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!--注入数据源-->
            <property name="dataSource" ref="dataSource">
            <!--加入配置文件-->
            <!--<property name="configLocation" value="classpath*:mybatis.cfg.xml"></property>-->
            <property name="configuration" ref="configuration">
            <!--配置mppaer.xml的扫描-->
            <property name="mapperLocations" value="classpath*:mapper/*Mapper.xml">
            <!--配置分页插件-->
            <property name="plugins">
                <bean class="com.github.pagehelper.PageInterceptor">
            </bean></property>
        </property></property></property></bean>
    
    
        <!--配置mapper接口的扫描-->
        <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.sxt.mapper">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory">
        </property></property></bean>
    
    </context:property-placeholder></beans>
    

    创建appliction-service.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: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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 扫描service.impl -->
        <context:component-scan base-package="com.sxt.service.impl">
    
        <!-- 配置事务管理器 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource">
        </property></bean>
    
        <!-- 配置事务的传播性 -->
        <tx:advice id="advice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="insert*" propagation="REQUIRED">
                <tx:method name="save*" propagation="REQUIRED">
                <tx:method name="update*" propagation="REQUIRED">
                <tx:method name="delete*" propagation="REQUIRED">
                <tx:method name="del*" propagation="REQUIRED">
                <tx:method name="change*" propagation="REQUIRED">
                <tx:method name="add*" propagation="REQUIRED">
                <tx:method name="get*" read-only="true">
                <tx:method name="load*" read-only="true">
                <tx:method name="query*" read-only="true">
            </tx:method></tx:method></tx:method></tx:method></tx:method></tx:method></tx:method></tx:method></tx:method></tx:method></tx:attributes>
        </tx:advice>
    
        <!-- 配置切面 -->
        <aop:config>
            <!-- 声明切面 -->
            <aop:pointcut id="pc" expression="execution(* com.sxt.service.impl.*.*(..))">
            <!-- 织入 -->
            <aop:advisor advice-ref="advice" pointcut-ref="pc">
        </aop:advisor></aop:pointcut></aop:config>
    </context:component-scan></beans>
    

    创建application-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" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 声明凭证匹配器 -->
        <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <!-- 散列算法名 -->
            <property name="hashAlgorithmName" value="md5">
            <!-- 散列次数 -->
            <property name="hashIterations" value="2">
        </property></property></bean>
    
        <!-- 创建realm -->
        <bean id="userRealm" class="com.sxt.realm.UserRealm">
            <!-- 注入凭证匹配器 -->
            <property name="credentialsMatcher" ref="credentialsMatcher">
        </property></bean>
    
        <!-- 声明安全管理器 -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <!-- 注入自定义realm -->
            <property name="realm" ref="userRealm">
        </property></bean>
    
        <!-- 声明过滤器 -->
        <!-- shiro的web过滤器, id必须和wen.xml里面的shiroFilter的targetBeanName的值一样 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <!-- 注入安全管理器 -->
            <property name="securityManager" ref="securityManager">
            <!-- 如果用户访问了需要认证的页面,而当前用户又没有认证时跳转的页面 -->
            <property name="loginUrl" value="/index.jsp">
            <!-- 用户登陆成功之后跳转的页面 (一般不用,因为我们是在Controller里面手动跳转的) -->
            <!-- <property name="successUrl" value="/success.jsp"/> -->
            <!-- 用户登录成功之后,访问没有权限的资源跳转的页面 -->
            <!-- <property name="unauthorizedUrl" value="/unauthorized.jsp"/> -->
            <!-- 配置过滤器资源 -->
            <property name="filterChainDefinitions">
                <value>
                    <!--配置放行的url-->
                    /index.jsp*=anon
                    /login/toLogin*=anon
                    /login/doLogin*=anon
                    <!--配置退出的url-->
                    /login/logout*=logout
                    <!--配置拦截的url-->
                    /**=authc
                </value>
            </property>
        </property></property></bean>
    
    </beans>
    

    创建applictionContext.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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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">
    
           <import resource="classpath*:application-dao.xml"></import>
           <import resource="classpath*:application-service.xml"></import>
           <import resource="classpath*:application-shiro.xml"></import>
    
    </beans>
    

    创建springmvc.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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemalocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        <!--扫描controller-->
        <context:component-scan base-package="com.sxt.controller">
    
        <!--配置映射器和适配器-->
        <mvc:annotation-driven>
    
        <!--配置视图解析器的前后缀-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!-- 注入前后缀 -->
            <property name="prefix" value="/WEB-INF/view/">
            <property name="suffix" value=".jsp">
        </property></property></bean>
    
        <!--配置文件上传-->
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <property name="defaultEncoding" value="utf-8">
            <property name="maxUploadSize" value="20971520">
            <property name="uploadTempDir" value="/upload/temp">
        </property></property></property></bean>
    
        <!-- 配置静态文件放行 -->
        <mvc:default-servlet-handler>
    </mvc:default-servlet-handler></mvc:annotation-driven></context:component-scan></beans>
    

    修改web.xml

    <!--?xml version="1.0" encoding="UTF-8"?-->
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemalocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
    
        <!-- shiro拦截的过滤器 -->
        <filter>
            <filter-name>delegatingFilterProxy</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>
            <init-param>
                <param-name>targetBeanName</param-name>
                <!--必须和application-shrio里面的 <bean id="shiroFilter"
                    class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">保持一至-->
                <param-value>shiroFilter</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>delegatingFilterProxy</filter-name>
            <servlet-name>springmvc</servlet-name>
        </filter-mapping>
    
        <!-- 配置编码过滤器 -->
        <filter>
            <filter-name>encFilter</filter-name>
            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
            <init-param>
                <param-name>encoding</param-name>
                <param-value>UTF-8</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>encFilter</filter-name>
            <servlet-name>springmvc</servlet-name>
        </filter-mapping>
    
        <!-- 配置加载applicationContext 监听器 -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:applicationContext.xml</param-value>
        </context-param>
    
        <!-- 配置前端控制器 -->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath*:springmvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>*.do</url-pattern>
        </servlet-mapping>
    
        <!-- 配置durid的控制页面 -->
        <servlet>
            <servlet-name>duridServlet</servlet-name>
            <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
            <init-param>
                <param-name>loginUsername</param-name>
                <param-value>admin</param-value>
            </init-param>
            <init-param>
                <param-name>loginPassword</param-name>
                <param-value>123456</param-value>
            </init-param>
        </servlet>
        <servlet-mapping>
            <servlet-name>duridServlet</servlet-name>
            <url-pattern>/durid/*</url-pattern>
        </servlet-mapping>
    
    </web-app>
    

    创建LoginController

    package com.sxt.controller;
    
    import com.sxt.common.ActiveUser;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.subject.Subject;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import javax.servlet.http.HttpServletRequest;
    
    @RequestMapping("login")
    @Controller
    public class LoginController {
    
        /**
         * 跳转到登陆页面
         * @return
         */
        @RequestMapping("toLogin")
        public String toLogin() {
            return "login";
        }
    
        /**
         * 登陆方法
         * @param username 用户名
         * @param password 密码
         * @param request
         * @return
         */
        @RequestMapping("doLogin")
        public String doLogin(String username, String password, HttpServletRequest request) {
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            Subject subject = SecurityUtils.getSubject();
            try {
                subject.login(token);
                ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
                request.getSession().setAttribute("username", activeUser.getUser().getUsername());
                return "list";
            } catch (AuthenticationException e) {
                request.setAttribute("error", "用户名或密码不正确");
                return "login";
            }
        }
    }
    

    创建index.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
        <title>Title</title>
    
    
        <jsp:forward page="/login/toLogin.do">
    
    
    

    创建WEB-INF/view/login.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
        <title>用户登陆</title>
    
    
    
    <h1 align="center">用户登陆</h1>
    <h3 align="center" style="color: red">${error}</h3>
    <form id="dataFrom" action="login/doLogin.do" method="post">
        <table align="center" cellpadding="5" cellspacing="5" border="2">
            <tbody><tr>
            <td>登陆名:</td>
            <td>
                <input type="text" name="username">
            </td>
        </tr>
            <tr>
                <td>密码:</td>
                <td>
                    <input type="password" name="password">
                </td>
            </tr>
            <tr>
                <td colspan="2" align="center">
                    <input type="submit" value="提交">
                </td>
            </tr>
        </tbody></table>
    </form>
    
    
    
    

    创建WEB-INF/view/list.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
    
    
        <title>列表</title>
    
    
        <shiro:authenticated>认证通过</shiro:authenticated>
        <shiro:hasrole name="超级管理员">当前用户是超级管理</shiro:hasrole>
    
        <shiro:haspermission name="person:query">
            <h3>查询</h3>
        </shiro:haspermission>
        <shiro:haspermission name="person:add">
            <h3>添加</h3>
        </shiro:haspermission>
    
        <shiro:haspermission name="person:update">
            <h3>修改</h3>
        </shiro:haspermission>
    
        <shiro:haspermission name="person:delete">
            <h3>删除</h3>
        </shiro:haspermission>
        <shiro:haspermission name="person:export">
            <h3>导出</h3>
        </shiro:haspermission>
    
    
    
    

    SSM+集成shiro+记住我

    复制05ssm的项目

    修改WEB-INF/view/login.jsp

    修改LoginContrller

    创建PersonContrller

    package com.sxt.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("person")
    public class PersonController {
    
        /**
         * 跳转到WEB-inf/view/list.jsp
         */
        @RequestMapping("toList")
        public String toList() {
            return "list";
        }
    }
    

    修改application-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" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--声明凭证匹配器-->
        <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <!--散列算法名-->
            <property name="hashAlgorithmName" value="md5">
            <!--散列次数-->
            <property name="hashIterations" value="2">
        </property></property></bean>
    
        <!--创建realm-->
        <bean id="userRealm" class="com.sxt.realm.UserRealm">
            <!--注入凭证匹配器-->
            <property name="credentialsMatcher" ref="credentialsMatcher">
        </property></bean>
        
        
        <!--声明cookie-->
        <bean id="cookie" class="org.apache.shiro.web.servlet.SimpleCookie">
            <!--设置cookie的名称  相当于Cookie: JSESSIONID=5B11D1E9C30684F5528F377D67963CE7  的JSESSIONID-->
            <constructor-arg name="name" value="rememberMe">
            <!-- 只有http请求时才能使用cookie -->
            <property name="httpOnly" value="true">
            <!-- 设置cookie的存活时间  7天 (单位:秒) -->
            <property name="maxAge" value="604800">
        </property></property></constructor-arg></bean>
        <!--声明记住我的管理器-->
        <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
            <property name="cookie" ref="cookie">
        </property></bean>
        
    
        <!--声明安全管理器-->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <!--注入自定义realm-->
            <property name="realm" ref="userRealm">
            
            <!--注入记住我的管理器-->
            <property name="rememberMeManager" ref="rememberMeManager">
            
        </property></property></bean>
    
        <!--声明过滤器-->
        <!-- Shiro 的Web过滤器 id必须和web.xml里面的shiroFilter的 targetBeanName的值一样 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    
            <!--注入安全管理器-->
            <property name="securityManager" ref="securityManager">
            <!--如果用户访问需要认证的页面,而当前用户又没有认证时跳转的页面-->
            <property name="loginUrl" value="/index.jsp">
            <!--如果用户登陆成功之后跳转的页面  一般不用,因为我们是在Controller里面手动跳转的-->
            <!--<property name="successUrl" value="/success.jsp"></property>-->
            <!--如果用户登陆成功之后 访问没有授权的资源 就跳转到这个页面-->
            <!--<property name="unauthorizedUrl" value="/unauthorized.jsp"></property>-->
    
            <!--注入自定义过滤器-->
            <property name="filters">
                <map>
                    <entry key="rememberMe">
                        <bean class="com.sxt.filter.RememberMeFilter">
    
                        </bean>
                    </entry>
                </map>
            </property>
    
            <!--配置过滤器资源-->
            <property name="filterChainDefinitions">
                <value>
                    <!--配置放行的url-->
                    /index.jsp*=anon
                    /login/toLogin*=anon
                    /login/doLogin*=anon
                    <!--配置退出的url-->
                    /login/logout*=logout
                    
                    <!--配置拦截的url-->
                    /**=rememberMe,user
                    /*=authc
                    /*/*=authc
                    
                </value>
            </property>
    
        </property></property></bean>
    </beans>
    

    测试

    先以记住我的方式登陆,成功之后再关闭浏览器,再请求http://127.0.0.1:8080/ssm-shiro/person/toList.do发现不用认证了,再清空cookie再请求一个要认证的地址 ,发现要认证。

    以上存在丢失session的问题

    解决丢失session的问题

    创建RememberMeFilter

    package com.sxt.filter;
    
    import com.sxt.common.ActiveUser;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    public class RememberMeFilter extends FormAuthenticationFilter {
    
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
            // 得到主体
            Subject subject = this.getSubject(request, response);
            // 从主体里面得到Session
            Session session = subject.getSession();
            // 判断session里面有没有username
            Object username = session.getAttribute("username");
            if (null == username) {
                // 在从主体里面取出身份ActiveUser
                ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
                if (null != activeUser) {
                    session.setAttribute("username", activeUser.getUser().getUsername());
                }
            }
            return true;    // true:代表放行  false:代表没人认证
        }
    }
    

    修改application-shiro.xml的配置

    ssm+shrio实现前后端分离

    前后端分离就是前端用一个项目,后端用一个项目。数据交互使用json串

    复制05ssm项目的内容到07ssmShrioAjax这个项目

    修改pom.xml

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
      <groupid>com.fasterxml.jackson.core</groupid>
      <artifactid>jackson-databind</artifactid>
      <version>${jackson.version}</version>
    </dependency>
    

    修改LoginController

    package com.sxt.controller;
    
    import com.sxt.common.ActiveUser;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.subject.Subject;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.HashMap;
    import java.util.Map;
    
    @RequestMapping("login")
    @Controller
    public class LoginController {
    
        // 跳转到登陆页面
        @RequestMapping("toLogin")
        public String toLogin(){
            return "login";
        }
    
        // 登陆方法
        @RequestMapping("doLogin")
        public Map<string, object=""> doLogin(String username, String password, HttpServletRequest request){
            Map<string,object> map = new HashMap<>();
            UsernamePasswordToken token=new UsernamePasswordToken(username,password);
            Subject subject = SecurityUtils.getSubject();
            try {
                subject.login(token);
                ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
                map.put("code", 200);
                map.put("msg", "用户登录成功");
                map.put("currentUser", activeUser.getUser());
                return map;
            }catch (AuthenticationException e){
                map.put("code", -1);
                map.put("msg","用户名或密码不正确");
                return map;
            }
        }
    }
    

    创建PersonController

    package com.sxt.controller;
    
    import org.apache.shiro.authz.annotation.RequiresPermissions;
    import org.apache.shiro.authz.annotation.RequiresRoles;
    import org.apache.shiro.authz.annotation.RequiresUser;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @RestController
    @RequestMapping("person")
    public class PersonController {
    
        /**
         * 要有user:query的权限才能调用
         * @return
         */
        @RequiresPermissions(value = {"person:query"})
        @RequestMapping("queryAllPerson")
        public Map<string,object> queryAllPerson(){
            Map<string,object> map = new HashMap<>();
            map.put("msg","person:query");
            return map;
        }
    
        /**
         * 修改
         * @return
         */
        @RequiresPermissions(value = {"person:update"})
        @RequestMapping("updatePerson")
        public Map<string,object> updatePerson(){
            Map<string,object> map = new HashMap<>();
            map.put("msg","person:update");
            return map;
        }
        /**
         * 添加
         * @return
         */
        @RequiresPermissions(value = {"person:add"})
        @RequestMapping("addPerson")
        public Map<string,object> addPerson(){
            Map<string,object> map = new HashMap<>();
            map.put("msg","person:add");
            return map;
        }
        
        /**
         * 删除
         * @return
         */
        @RequiresPermissions(value = {"person:delete"})
        @RequestMapping("deletePerson")
        public Map<string,object> deletePerson(){
            Map<string,object> map = new HashMap<>();
            map.put("msg","person:delete");
            return map;
        }
        
        /**
         * 导出
         * @return
         */
        @RequiresPermissions(value = {"person:export"})
        @RequestMapping("exportPerson")
        public Map<string,object> exportPerson(){
            Map<string,object> map = new HashMap<>();
            map.put("msg","person:export");
            return map;
        }
    
    }
    

    以zhangsan登陆出现personExport方法还能调用

    zhangsan登陆出现personExport方法还能调用的问题

    因为我们现在使用的注解方法

    @RequiresPermissions(value = {"user:delete"}) 代表当前方法被调用时当前用户必有user:delete的权限

    @RequiresRoles(value = "普通管理员") 代表当前方法被调用时当前用户必有普通管理员的的角色

    启动shiro的注解

    修改springmvc.xml

    image

    测试以zhangsan登陆

    发现persion:query person:add person:update person:delete可以调用

    发现person:export 不能调用 出现未授权的异常

    image

    处理授权的异常 以json串的形式返回出去 使用Springmvc的全异常

    创建GlobExceptionAspect

    package com.sxt.common;
    
    import org.apache.shiro.authz.UnauthorizedException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @RestControllerAdvice // 如果出现异常,会返回一个json字符串
    //@ControllerAdvice //如果出现异常,会跳转到某个页面
    public class GlobExceptionAspect {
    
        @ExceptionHandler(UnauthorizedException.class)
        public Map<string, object=""> unAuthorized(){
            Map<string, object=""> map = new HashMap<>();
            map.put("code", 302);
            map.put("msg","没有调用权限");
            return map;
        }
    
    }
    

    扫描异常类

    image

    zhangsan登陆清空session之后返回未登陆的处理

    重写authc的过滤器,修改ShiroLoginFilter

    package com.sxt.filter;
    
    import com.alibaba.fastjson.JSON;
    import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    public class ShiroLoginFilter extends FormAuthenticationFilter {
        /**
         * 在访问controller前判断是否登录,返回json,不进行重定向。
         * @param request
         * @param response
         * @return true-继续往下执行,false-该filter过滤器已经处理,不继续执行其他过滤器
         * @throws Exception
         */
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;
            //if (isAjax(request)) {
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("application/json");
            Map<string,object> resultData = new HashMap<>();
            resultData.put("code", -1);
            resultData.put("msg", "登录认证失效,请重新登录!");
            httpServletResponse.getWriter().write(JSON.toJSON(resultData).toString());
    	 /* } else {
    			// saveRequestAndRedirectToLogin(request, response);
             	// @Mark 非ajax请求重定向为登录页面
    			httpServletResponse.sendRedirect("/login");
    		}*/
            return false;
        }
    
        private boolean isAjax(ServletRequest request) {
            String header = ((HttpServletRequest) request).getHeader("X-Requested-With");
            if ("XMLHttpRequest".equalsIgnoreCase(header)) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }
    
    }
    

    修改application-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" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 声明凭证匹配器 -->
        <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <!-- 散列算法名 -->
            <property name="hashAlgorithmName" value="md5">
            <!-- 散列次数 -->
            <property name="hashIterations" value="2">
        </property></property></bean>
    
        <!-- 创建realm -->
        <bean id="userRealm" class="com.sxt.realm.UserRealm">
            <!-- 注入凭证匹配器 -->
            <property name="credentialsMatcher" ref="credentialsMatcher">
        </property></bean>
    
        <!-- 声明安全管理器 -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <!-- 注入自定义realm -->
            <property name="realm" ref="userRealm">
        </property></bean>
    
        <!-- 声明过滤器 -->
        <!-- shiro的web过滤器, id必须和wen.xml里面的shiroFilter的targetBeanName的值一样 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <!-- 注入安全管理器 -->
            <property name="securityManager" ref="securityManager">
            <!-- 如果用户访问了需要认证的页面,而当前用户又没有认证时跳转的页面 -->
            <!-- <property name="loginUrl" value="/index.jsp"/> -->
            <!-- 用户登陆成功之后跳转的页面 (一般不用,因为我们是在Controller里面手动跳转的) -->
            <!-- <property name="successUrl" value="/success.jsp"/> -->
            <!-- 用户登录成功之后,访问没有权限的资源跳转的页面 -->
            <!-- <property name="unauthorizedUrl" value="/unauthorized.jsp"/> -->
    
            <!-- 重写authc的过滤器 -->
            <property name="filters">
                <map>
                    <entry key="authc">
                        <bean class="com.sxt.filter.ShiroLoginFilter">
    
                        </bean>
                    </entry>
                </map>
            </property>
    
            <!-- 配置过滤器资源 -->
            <property name="filterChainDefinitions">
                <value>
                    <!--配置放行的url-->
                    /login/doLogin*=anon
                    <!--配置退出的url-->
                    /login/logout*=logout
                    <!--配置拦截的url-->
                    /**=authc
                </value>
            </property>
        </property></bean>
    
    </beans>
    

    测试方法

    以张三登陆,访问http://127.0.0.1:8080/bjsxt/person/exportPerson.do 可以访问,但是调用权限。

    清空浏览器缓存

    访问http://127.0.0.1:8080/bjsxt/person/exportPerson.do 可以访问,但是返回的没有登陆。

    Spring Boot整合Shiro实现登录认证

    Spring Boot整合Shiro实现登录认证+记住我+退出(源码)https://www.lanzous.com/ib3kydg

    修改pom.xml

    <!--?xml version="1.0" encoding="UTF-8"?-->
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelversion>4.0.0</modelversion>
    
        <groupid>com.shiro</groupid>
        <artifactid>09shiro_springboot_login</artifactid>
        <version>1.0-SNAPSHOT</version>
    
        <!--配置继承-->
        <parent>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-parent</artifactid>
            <version>2.1.10.RELEASE</version>
        </parent>
    
        <!--配置依赖-->
        <dependencies>
            <!--配置web启动器-->
             <dependency>
                 <groupid>org.springframework.boot</groupid>
                 <artifactid>spring-boot-starter-web</artifactid>
             </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
            <dependency>
                <groupid>org.projectlombok</groupid>
                <artifactid>lombok</artifactid>
                <version>1.18.10</version>
                <scope>provided</scope>
            </dependency>
    
    
            <!--配置mybatis启动器-->
            <dependency>
                <groupid>org.mybatis.spring.boot</groupid>
                <artifactid>mybatis-spring-boot-starter</artifactid>
                <version>2.1.1</version>
            </dependency>
    
            <!--配置mysql驱动-->
            <dependency>
                <groupid>mysql</groupid>
                <artifactid>mysql-connector-java</artifactid>
                <version>5.1.48</version>
            </dependency>
    
            <!--配置Thrmeleaf启动器-->
            <dependency>
                <groupid>org.springframework.boot</groupid>
                <artifactid>spring-boot-starter-thymeleaf</artifactid>
            </dependency>
    
            <!--配置shiro的启动器-->
            <dependency>
                <groupid>org.apache.shiro</groupid>
                <artifactid>shiro-spring-boot-web-starter</artifactid>
                <version>1.4.2</version>
            </dependency>
    
            <!--添加shiro整合thymeleaf的依赖-->
            <dependency>
                <groupid>com.github.theborakompanioni</groupid>
                <artifactid>thymeleaf-extras-shiro</artifactid>
                <version>2.0.0</version>
            </dependency>
    
            <!--配置SpringBoot整合EHCache的依赖-->
            <dependency>
                <groupid>org.apache.shiro</groupid>
                <artifactid>shiro-ehcache</artifactid>
                <version>1.4.2</version>
            </dependency>
    
            <dependency>
                <groupid>commons-io</groupid>
                <artifactid>commons-io</artifactid>
                <version>2.6</version>
            </dependency>
        </dependencies>
    
    </project>
    

    创建application.yml

    spring:
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://47.110.81.221:3310/shiro
        username: root
        password: root
    #配置mapper的xml文件的路径
    mybatis:
      mapper-locations: classpath:mybatis/*.xml
    shiro:
      #当用户访问某个需要登录的功能时,但是被shiro内置的过滤器拦截后,判断本次请求
      #没有登录,而是直接访问的,则重定向到loginUrl的路径资源响应给用户
      loginUrl: /login
    

    创建ehcache-shiro.xml

    <!--?xml version="1.0" encoding="UTF-8"?-->
    <ehcache name="ehcache" updatecheck="false">
    
        <!-- 磁盘缓存位置 -->
        <diskstore path="java.io.tmpdir">
        <!-- 默认缓存 -->
        <defaultcache maxentrieslocalheap="1000" eternal="false" timetoidleseconds="3600" timetoliveseconds="3600" overflowtodisk="false">
        </defaultcache>
    
        <!-- 登录记录缓存 锁定10分钟 -->
        <cache name="loginRecordCache" maxentrieslocalheap="2000" eternal="false" timetoidleseconds="600" timetoliveseconds="0" overflowtodisk="false" statistics="true">
        </cache>
    
    </diskstore></ehcache>
    

    创建User实体类

    package com.shiro.domain;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        /** 用户ID */
        private Integer uid;
    
        /** 用户名 */
        private String uname;
    
        /** 密码 */
        private String pwd;
    }
    

    创建UserMapper

    package com.shiro.mapper;
    
    import com.shiro.domain.User;
    import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.annotations.Select;
    
    public interface UserMapper {
    
        /**
         * 根据用户名查询用户信息
         */
        @Select("select * from User where uname=#{uname}")
        User selUserInfoMapper(@Param("uname") String uname);
    
    }
    

    编写业务层代码

    创建UserService接口

    package com.shiro.service;
    
    import com.shiro.domain.User;
    
    public interface UserService {
    
        /**
         * 用户登录
         * @param uname 用户名
         * @return
         */
        User selUserInfoService(String uname);
    
    }
    

    创建UserServiceImpl实现类

    package com.shiro.service.impl;
    
    import com.shiro.mapper.UserMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import com.shiro.domain.User;
    import com.shiro.service.UserService;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        /** 声明mapper属性 */
        @Autowired
        private UserMapper userMapper;
    
        /**
         * 用户登录
         * @param uname 用户名
         * @return
         */
        @Override
        public User selUserInfoService(String uname) {
            return userMapper.selUserInfoMapper(uname);
        }
    
    }
    

    自定义Realm

    package com.shiro.shiro;
    
    import com.shiro.domain.User;
    import com.shiro.service.UserService;
    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.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyRealm extends AuthorizingRealm {
        /** 声明业务层属性 */
        @Autowired
        private UserService userService;
    
        /**
         * 自定义授权策略
         *      1.从数据库中获取用户的权限信息
         *      2.将权限信息存储到shiro授权对象中
         * @param principals
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            System.out.println("我是授权认证方法,我被执行了!");
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.addRole("role1");
            info.addRole("role2");
            info.addStringPermission("user:insert");
            info.addStringPermission("user:update");
            info.addStringPermission("sys:*");
            return info;
        }
    
        /**
         * 自定义认证策略
         * @param token
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            //声明认证代码
            //1.获取用户传递的用户名信息
            Object principal = token.getPrincipal();
            //2.根据用户名获取数据库中的用户信息
            User user = userService.selUserInfoService((String) principal);
            //3.认证
            if (user != null) { //用户名是正确的
                //4.认证密码
                AuthenticationInfo info = new SimpleAuthenticationInfo(principal, user.getPwd(), ByteSource.Util.bytes(user.getUid() + ""), user.getUname());
                return info;
            }
            return null;
        }
    }
    

    编写配置-ShiroConfig

    package com.shiro.config;
    
    import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
    import com.shiro.shiro.MyRealm;
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.cache.ehcache.EhCacheManager;
    import org.apache.shiro.codec.Base64;
    import org.apache.shiro.io.ResourceUtils;
    import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
    import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
    import org.apache.shiro.web.mgt.CookieRememberMeManager;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.servlet.SimpleCookie;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.io.InputStream;
    
    @Configuration
    public class ShiroConfig {
    
        /** 声明MyRealm属性 */
        @Autowired
        private MyRealm myRealm;
    
        /**
         * 声明bean方法
         * @return
         */
        @Bean
        public DefaultWebSecurityManager securityManager() {
            DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
            //创建凭证匹配器
            HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
            //设置匹配器的加密算法
            matcher.setHashAlgorithmName("md5");
            //设置匹配器的迭代加密次数
            matcher.setHashIterations(2);
            //将匹配器注入到自定义的认证策略对象中
            myRealm.setCredentialsMatcher(matcher);
            //将自定义的认证策略对象注入到SecurityManager
            defaultWebSecurityManager.setRealm(myRealm);
            //将CookieRememberMeManager对象注入到SecurityManager,开启了rememberMe功能
            defaultWebSecurityManager.setCacheManager(ehCacheManager());
            return defaultWebSecurityManager;
        }
    
        /**
         * 设置Cookie的信息
         * @return
         */
        private SimpleCookie rememberMeCookie() {
            SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
            //设置有效路径
            simpleCookie.setPath("/");
            //设置声明周期(7天)
            simpleCookie.setMaxAge(7 * 24 * 60 * 60);
            //返回设置的cookie
            return simpleCookie;
        }
    
        /**
         * 创建rememberMeManager对象
         * @return
         */
        public CookieRememberMeManager rememberMeManager() {
            //创建CookieRememberMeManager对象
            CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
            //注入Cookie对象
            cookieRememberMeManager.setCookie(rememberMeCookie());
            //设置密钥
            cookieRememberMeManager.setCipherKey(Base64.decode("MTIzNDU2Nzg="));
            //返回
            return cookieRememberMeManager;
        }
    
        /**
         * 自定义shiro过滤器参数bean
         * @return
         */
        @Bean
        public ShiroFilterChainDefinition shiroFilterChainDefinition() {
            DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
            definition.addPathDefinition("/login", "anon");
            definition.addPathDefinition("/userLogin2", "anon");
            //开启shiro内置的退出过滤器,完成退出功能
            definition.addPathDefinition("/logout", "logout");
            //definition.addPathDefinition("/main", "anon");
            definition.addPathDefinition("/**", "user");
            return definition;
        }
    
        /**
         * 创建Bean方法,创建CarManager对象
         * @return
         */
        @Bean
        public EhCacheManager ehCacheManager() {
            //创建ehCacheManager对象
            EhCacheManager ehCacheManager = new EhCacheManager();
            //获取配置文件的流对象
            InputStream is = null;
            try {
                is = ResourceUtils.getInputStreamForPath("classpath:ehcache/ehcache-shiro.xml");
            } catch (Exception e) {
                e.printStackTrace();
            }
            //获取CarManager对象
            net.sf.ehcache.CacheManager cacheManager = new net.sf.ehcache.CacheManager(is);
            ehCacheManager.setCacheManager(cacheManager);
            //返回
            return ehCacheManager;
        }
    
        /**
         * 创建解析Thymeleaf中的shiro属性的对象,由SpringBoot项目底层自动调用
         * @return
         */
        @Bean
        public ShiroDialect shiroDialect() {
            return new ShiroDialect();
        }
    
    }
    

    配置无权限异常

    package com.shiro.controller;
    
    import org.apache.shiro.authz.AuthorizationException;
    import org.apache.shiro.authz.UnauthorizedException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @ControllerAdvice
    public class NoPermissionException {
    
        @ResponseBody
        @ExceptionHandler(UnauthorizedException.class)
        public String handleShiroException(Exception e) {
            return "无权限";
        }
    
        @ResponseBody
        @ExceptionHandler(AuthorizationException.class)
        public String AuthorizationException(Exception e) {
            return "权限认证失败";
        }
    
    }
    

    编写控制器-UserController

    package com.shiro.controller;
    
    import com.shiro.domain.User;
    import com.shiro.service.UserService;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authz.annotation.RequiresPermissions;
    import org.apache.shiro.subject.Subject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class UserController {
    
        /** 声明service属性 */
        @Autowired
        private UserService userService;
    
        /**
         * 声明单元方法:使用shiro认证
         * @param uname 用户名
         * @param pwd 密码
         * @param rememberMe 记住我
         * @return
         */
        @RequestMapping("userLogin2")
        public String userLogin2(String uname, String pwd, @RequestParam(defaultValue = "false") Boolean rememberMe) {
            //1.获取subject对象
            Subject subject = SecurityUtils.getSubject();
            //2.认证
            //创建认证对象存储认证信息
            AuthenticationToken token = new UsernamePasswordToken(uname, pwd, rememberMe);
            try {
                subject.login(token);
                return "redirect:main";
            } catch (Exception e) {
                e.printStackTrace();
            }
            return "redirect:login";
        }
    
        /**
         * 声明单元方法:登录认证
         * @param uname 用户名
         * @param pwd 密码
         * @return
         */
        @RequestMapping("userLogin")
        public String userLogin(String uname, String pwd) {
            //1.根据用户名获取用户信息
            User user = userService.selUserInfoService(uname);
            //2.判断用户名是否合法
            if (user != null) {
                //3.校验密码
                if (user.getPwd().equals(pwd)) {
                    //认证成功
                    return "main";
                }
            }
            return "error";
        }
    
        //声明单元方法:
        @RequiresPermissions("user:insert")
        @ResponseBody
        @RequestMapping("demo")
        public String demo() {
            return "ok";
        }
    
        //声明公共单元方法完成页面的内部转发
        @RequestMapping("{uri}")
        public String getPage(@PathVariable String uri) {
            return uri;
        }
    
    }
    

    修改启动类

    package com.shiro;
    
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    @MapperScan("com.shiro.mapper")
    public class  ShiroApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ShiroApplication.class, args);
        }
    
    }
    

    编写页面

    编写登录页面-login.html

    
    
        <title>用户登陆</title>
    
    
    
    <h1 align="center">用户登陆</h1>
    <form id="dataFrom" action="userLogin2" method="post">
        <table align="center" cellpadding="5" cellspacing="5" border="2">
            <tbody><tr>
                <td>登录名:</td>
                <td>
                    <input type="text" name="uname" value="">
                </td>
            </tr>
            <tr>
                <td>密码:</td>
                <td>
                    <input type="password" name="pwd" value="">
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="checkbox" name="rememberMe" value="true">记住我
                </td>
            </tr>
            <tr>
                <td colspan="2" align="center">
                    <input type="submit" value="登录">
                </td>
            </tr>
        </tbody></table>
    </form>
    
    
    
    

    编写主页面-main.html

    
    
    
        <meta charset="UTF-8">
        <title>Title</title>
    
    
        <h3>我是主页面</h3>
        <a href="/logout">退出</a>
        <hr>
        <span shiro:hasrole="role3">有角色</span>
        <br>
        <a href="/demo">测试后台逻辑代码的授权</a>
    
    
    

    EHCache

    ehcache简介

    EHCache是sourceforge的开源缓存项目,现已经具有独立官网,网址:(http://www.ehcache.org)。其本身是纯JAVA实现的,所以可以和绝大多数Java项目无缝整合,例如:Hibernate的缓存就是基于EHCache实现的。

    EHCache支持内存和磁盘缓存,默认是存储在内存中的,当内存不够时允许把缓存数据同步到磁盘中,所以不需要担心内存不够问题。

    EHCache支持基于Filter的Cache实现,同时也支持Gzip压缩算法提高响应速度。

    EHCache API演示

    1. 添加依赖

    3.0版本开始groupidorg.ehcache

    <dependencies>
        <dependency>
            <groupid>net.sf.ehcache</groupid>
            <artifactid>ehcache</artifactid>
            <version>2.6.11</version>
        </dependency>
    </dependencies>
    

    2. 新建配置文件

    在src/main/resources中新建ehcache.xml。

    属性含义:

    maxElementsInMemory:缓存中允许创建的最大对象数。

    eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。

    timeToIdleSeconds:缓存数据的钝化时间,取值0表示无限长。

    timeToLiveSeconds:缓存数据的生存时间,取值0表示无限长。

    overflowToDisk:内存不足时,是否启用磁盘缓存。

    memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。

    <!--?xml version="1.0" encoding="UTF-8"?-->
    <ehcache>
        <!-- 磁盘缓存位置 -->
        <diskstore path="java.io.tmpdir/ehcache">
        <!-- 默认缓存 -->
        <defaultcache maxentrieslocalheap="10000" eternal="false" timetoidleseconds="120" timetoliveseconds="120" maxentrieslocaldisk="10000000" diskexpirythreadintervalseconds="120" memorystoreevictionpolicy="LRU">
            <persistence strategy="localTempSwap">
        </persistence></defaultcache>
        <!-- helloworld缓存 -->
        <cache name="HelloWorldCache" maxelementsinmemory="1000" eternal="false" timetoidleseconds="5" timetoliveseconds="5" overflowtodisk="false" memorystoreevictionpolicy="LRU">
    </cache></diskstore></ehcache>
    

    Shiro和EhCache整合

    Shiro支持很多第三方缓存工具。官方提供了shiro-ehcache,实现了把EHCache当做Shiro的缓存工具的解决方案。其中最好用的一个功能是就是缓存认证执行的Realm方法,减少对数据库的访问。

    1. 添加依赖

    添加shiro-ehcache依赖。

    commons-io主要是为了使用里面的工具类。本质和当前整合功能没有关系。

    <dependency>
        <groupid>org.apache.shiro</groupid>
        <artifactid>shiro-ehcache</artifactid>
        <version>1.4.2</version>
    </dependency>
    <dependency>
        <groupid>commons-io</groupid>
        <artifactid>commons-io</artifactid>
        <version>2.6</version>
    </dependency>
    

    2. 编写ehcache缓存配置

    在resources下新建ehcache/ehcache-shiro.xml

    <!--?xml version="1.0" encoding="UTF-8"?-->
    <ehcache name="ehcache" updatecheck="false">
    
    <!-- 磁盘缓存位置 -->
    <diskstore path="java.io.tmpdir">
    
    <!-- maxEntriesLocalHeap:堆内存中最大缓存对象数,0没有限制 -->
    <!-- maxElementsInMemory: 在内存中缓存的element的最大数目。-->
    <!-- eternal:elements是否永久有效,如果为true,timeouts将被忽略,element将永不过期 -->
    <!-- timeToIdleSeconds:失效前的空闲秒数,当eternal为false时,这个属性才有效,0为不限制 -->
    <!-- timeToLiveSeconds:失效前的存活秒数,创建时间到失效时间的间隔为存活时间,当eternal为false时,这个属性才有效,0为不限制 -->
    <!-- overflowToDisk: 如果内存中数据超过内存限制,是否要缓存到磁盘上 -->
    <!-- statistics:是否收集统计信息。如果需要监控缓存使用情况,应该打开这个选项。默认为关闭(统计会影响性能)。设置statistics="true"开启统计 -->
    
    <!-- 默认缓存 -->
    <defaultcache maxentrieslocalheap="1000" eternal="false" timetoidleseconds="3600" timetoliveseconds="3600" overflowtodisk="false">
    </defaultcache>
    
    <!-- 登录记录缓存 锁定10分钟 -->
    <cache name="loginRecordCache" maxentrieslocalheap="2000" eternal="false" timetoidleseconds="600" timetoliveseconds="0" overflowtodisk="false" statistics="true">
    </cache>
    
    </diskstore></ehcache>
    

    3. 修改配置文件

    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        hashedCredentialsMatcher.setHashIterations(2);
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        manager.setRealm(myRealm);
        manager.setRememberMeManager(rememberMeManager());
        manager.setCacheManager(getEhCacheManager());
        return manager;
    }
    
    @Bean
    public EhCacheManager ehCacheManager(){
        EhCacheManager ehCacheManager = new EhCacheManager();
        InputStream is = null;
        try {
            is = ResourceUtils.getInputStreamForPath("classpath:ehcache/ehcache-shiro.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        net.sf.ehcache.CacheManager cacheManager = new net.sf.ehcache.CacheManager(is);
        ehCacheManager.setCacheManager(cacheManager);
        return ehCacheManager;
    }
    

    Shiro中Session对象获取

    Session session = SecurityUtils.getSubject().getSession();
    session.setAttribute("key","value");
    

    </string,object></string,></string,></string,object></string,object></string,object></string,object></string,object></string,object></string,object></string,object></string,object></string,object></string,object></string,></jsp:forward>

  • 相关阅读:
    AutoIT练习
    AutoIT键盘鼠标模拟组件
    javascript class 定义
    JIRA
    vs 命令窗口 常用命令
    jquery 获取参数 plugin
    THE VALUE OF TIME
    哈佛图书馆的二十条训言
    优秀javascript js组件集锦
    UVA 11205 The broken pedometer
  • 原文地址:https://www.cnblogs.com/lyang-a/p/15089804.html
Copyright © 2020-2023  润新知