Shiro

爷,独闯天下 提交于 2020-03-25 21:07:32

Shiro

Shiro概述

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

基本功能

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即可。

架构说明

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 使用时可以连接数据库,也可以不连接数据库

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

Shrio.ini 文件的组成部分

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

  1. 内置securityManager对象.

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

[main]
securityManager.属性=值
myobj=com.bjsxt.lei
securityManager.对象属性=$myobj

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

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

3. [roles]:定义角色

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

4. [urls]:定义哪些内置urls生效(在web应用时使用)

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

过滤器

过滤器名称 过滤器类 描述
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组合就是用户名/密码了。接下来先进行一个基本的身份认证

认证流程

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

创建Maven项目

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

在resources目录下面创建shiro.ini

修改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. 授权流程

相关方法说明

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

​​

修改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("您未登陆");
        }

    }
}

自定义Realm实现认证

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

Realm接口

IniRealm的执行流程

解析shiro.ini

做认证和授权

总结

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

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

自定义realm做认证

创建项目

修改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一样,大部分情况是要从数据库获取权限数据,这里直接实现基于资源的授权。

创建项目

修改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+集成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;

创建项目

生成User​

生成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}"/>
    </bean>

    <!--声明配置对象-->
    <bean id="configuration" class="org.apache.ibatis.session.Configuration">
        <!--在控制台输出sql-->
        <property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
    </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"/>
        </property>
    </bean>


    <!--配置mapper接口的扫描-->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.sxt.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </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: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"/>
    </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:attributes>
    </tx:advice>

    <!-- 配置切面 -->
    <aop:config>
        <!-- 声明切面 -->
        <aop:pointcut id="pc" expression="execution(* com.sxt.service.impl.*.*(..))"/>
        <!-- 织入 -->
        <aop:advisor advice-ref="advice" pointcut-ref="pc"/>
    </aop:config>
</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"/>
    </bean>

    <!-- 创建realm -->
    <bean id="userRealm" class="com.sxt.realm.UserRealm">
        <!-- 注入凭证匹配器 -->
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>

    <!-- 声明安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- 注入自定义realm -->
        <property name="realm" ref="userRealm"/>
    </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>
    </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"/>
    </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"/>
    </bean>

    <!-- 配置静态文件放行 -->
    <mvc:default-servlet-handler/>
</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" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <jsp:forward page="/login/toLogin.do"/>
</body>
</html>

创建WEB-INF/view/login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>用户登陆</title>
</head>
<body>

<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">
        <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>
    </table>
</form>

</body>
</html>

创建WEB-INF/view/list.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<head>
    <title>列表</title>
</head>
<body>
    <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>

</body>
</html>

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

    <!--创建realm-->
    <bean id="userRealm" class="com.sxt.realm.UserRealm">
        <!--注入凭证匹配器-->
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </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"/>
    </bean>
    <!--声明记住我的管理器-->
    <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cookie" ref="cookie"/>
    </bean>
    

    <!--声明安全管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--注入自定义realm-->
        <property name="realm" ref="userRealm"/>
        
        <!--注入记住我的管理器-->
        <property name="rememberMeManager" ref="rememberMeManager"/>
        
    </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>

    </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实现前后端分离

复制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

测试以zhangsan登陆

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

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

处理授权的异常 以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;
    }

}

扫描异常类

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

    <!-- 创建realm -->
    <bean id="userRealm" class="com.sxt.realm.UserRealm">
        <!-- 注入凭证匹配器 -->
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>

    <!-- 声明安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- 注入自定义realm -->
        <property name="realm" ref="userRealm"/>
    </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>
    </bean>

</beans>

测试方法

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

清空浏览器缓存

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!