学习Shiro

Shiro

Shiro 简介

Apache Shiro 是 Java 的一个安全框架。对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。

Shiro 不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。

Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。、

下载地址

其基本功能点如下图所示:

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

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

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

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

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

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

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

Testing:提供测试支持;

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

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

Shiro 的架构

外部来看 Shiro ,如下图:

可以看到:应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外 API 核心就是 Subject。

其每个 API 的含义:

Subject:主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;

SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

Realm:域,Shiro 从从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。

也就是说对于我们而言,最简单的一个 Shiro 应用:

  1. 应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;
  2. 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。

从以上也可以看出,Shiro 不提供维护用户 / 权限,而是通过 Realm 让开发人员自己注入。

内部Shiro 的架构,如下图所示:

Subject:主体,可以看到主体可以是任何可以与应用交互的 “用户”;

SecurityManager:相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。

Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;

Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;

Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;

SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所以呢,Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);

SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把 Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能;

CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能

Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密 / 解密的。

shiro组件

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

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

principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个 Primary principals,一般是用户名 / 密码 / 手机号。

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

最常见的 principals 和 credentials 组合就是用户名 / 密码了。

另外两个相关的概念是之前提到的 SubjectRealm,分别是主体及验证主体的数据源。

shiro默认过滤器

shiro提供和多个默认的过滤器,我们可以用这些过滤器来配置过滤指定url的访问权限。

配置缩写 对应的过滤器 功能
anon AnonymousFilter 指定url可以匿名访问
authc FormAuthenticationFilter 指定url需要form表单登录,默认会从请求中获取username、password,rememberMe等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器做默认的登录逻辑,但是一般都是我们自己在控制器写登录逻辑的,自己写的话出错返回的信息都可以定制嘛。
authcBasic BasicHttpAuthenticationFilter 指定url需要basic登录
logout LogoutFilter 登出过滤器,配置指定url就可以实现退出功能,非常方便
noSessionCreation NoSessionCreationFilter 禁止创建会话
perms PermissionsAuthorizationFilter 需要指定权限才能访问
port PortFilter 需要指定端口才能访问
rest HttpMethodPermissionFilter 将http请求方法转化成相应的动词来构造一个权限字符串,这个感觉意义不大,有兴趣自己看源码的注释
roles RolesAuthorizationFilter 需要指定角色才能访问
ssl SslFilter 需要https请求才能访问
user UserFilter 需要已登录或“记住我”的用户才能访问

shiro常用的权限控制注解,可以在控制器类上使用

注解 功能
@RequiresGuest 只有游客可以访问
@RequiresAuthentication 需要登录才能访问
@RequiresUser 已登录的用户或“记住我”的用户能访问
@RequiresRoles 已登录的用户需具有指定的角色才能访问
@RequiresPermissions 已登录的用户需具有指定的权限才能访问

@RequireXXX注解可能的Bug

注意:解决spring aop和注解配置一起使用的bug。如果您在使用shiro注解配置的同时,引入了spring aop的starter,会有一个奇怪的问题,导致shiro注解的请求,不能被映射,需加入以下配置:

@Bean
public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
    DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
    /**
         * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
         * 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,会导致该方法无法映射请求,导致返回404。 加入这项配置能解决这个bug
         */
    defaultAdvisorAutoProxyCreator.setUsePrefix(true);
    //defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
    return defaultAdvisorAutoProxyCreator;
}

整合Shiro

1.Shiro 相关的依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.6.0</version>
</dependency>

2.自定义核心组件 Realm

public class MyRealm extends AuthorizingRealm {
    @Autowired
    UserMapper userMapper;

    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        User user = (User) getAvailablePrincipal(principals);
        // 获取权限
        String[] perms = user.getPerms().trim().split(",");
        List<String> permList = Arrays.asList(perms);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 添加权限
        info.addStringPermissions(permList);
        return info;
    }
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken uToken = (UsernamePasswordToken) token;
        String username = uToken.getUsername();
        User user = userMapper.userFindByName(username);
        if (user == null) {
            return null;
        }
        // 默认使用SimpleCredentialsMatcher密码匹配
        return new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(username),getName());
    }
}

AuthorizingRealm

AuthorizingRealm 类继承自 AuthenticatingRealm。但没有实现 AuthenticatingRealm 中的 doGetAuthenticationInfo,所以认证和授权只需要继承 AuthorizingRealm就可以了。

自定义Realm继承AuthorizingRealm,同时实现 doGetAuthenticationInfo 方法和doGetAuthorizationInfo方法 ,即可实现认证和授权操作。

2.1方法执行时机

doGetAuthenticationInfo执行时机(登录信息验证)

当调用Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);

doGetAuthorizationInfo执行时机有三个(角色与权限)

1、subject.hasRole(“admin”)subject.isPermitted(“admin”)自己去调用这个是否有什么角色或者是否有什么权限的时候;

2、@RequiresRoles("admin") 在方法上加注解的时候;

3、@shiro.hasPermission name = "admin"/@shiro.hasPermission:"dustin:test"在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。

//------------------------何时调用认证方法------------------
1、subject.login();  //每次调用shiro提供的login()方法,就会调用1次认证方法。多次调用login()方法,就多次执行认证方法。

//------------------------何时调用授权方法------------------
//=========方法=============
1、hasRole("admin");  //调用1次授权方法
2、hasRoles(Arrays.asList("admin", "guest");  //调用2次授权方法
3、isPermitted("product:view");  //调用1次授权方法
4、isPermitted("product:view", "product:edit", "product:create", "product:remove");  //调用4次授权方法
//=========注解=============
5、@RequiresRoles(logical = Logical.OR, value = {"user", "admin"})  //调用2次授权方法。
6、@RequiresPermissions(logical = Logical.OR, value = {"product:view", "product:edit"})  //调用2次授权方法

注意: 没有使用缓存,也就意味这要调用多次数据库查询用户的权限。使用 缓存 可以实现 用户认证成功的时候调用一下授权方法,后续不再调用认证 方法。

2.1认证相关类

  • AuthenticationToken (认证token)

    UsernamePasswordToken实现了AuthenticationToken,存放了一些用户登录的信息,可以在用户登录时设置密码,rememberMe等

    UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    token.setRememberMe(true);
    
  • AuthenticationInfo (认证info)

    SimpleAuthenticationInfo实现了AuthenticationInfo,用户帐号比对后,将查询后的用户信息存放到SimpleAuthenticationInfo中(包含了身份及凭证),如果验证失败将抛出相应的AuthenticationException异常。

    public SimpleAuthenticationInfo(Object principal, Object credentials, String realmName) {
    this.principals = new SimplePrincipalCollection(principal, realmName);
    this.credentials = credentials;
    }
    

2.2授权相关类

  • AuthorizationInfo (授权info)

    SimpleAuthorizationInfo实现了AuthorizationInfo,可通过addStringPermission()addRole()方法添加权限和角色。

  • PrincipalCollection

    与对应的Subject相关联的所有principal的集合。

    principal只是一个安全术语,用于识别属性,例如用户名、用户ID、社会安全号或其他任何可以被认为是Subject的 "识别 "属性。

    一个PrincipalCollection根据Realm来组织它的内部Principal,当Subject第一次被创建时,它们来自于Realm。 要想获得特定Realm的Principal(s),请参阅{@link #fromRealm}方法。

    • 在自定义realm中可通getAvailablePrincipal()获取当前实体对象User.
    User user = (User) getAvailablePrincipal(principals);
    
    • 也可以用SecurityUtils.getSubject().getPrincipal()拿出用户的所有信息,包括角色和权限
    User principal = (User) SecurityUtils.getSubject().getPrincipal();
    

Shiro 配置类

@Configuration
public class ShiroConfig {
    @Bean
    MyRealm myRealm() {
        return new MyRealm();
    }
    
    @Bean
    SecurityManager securityManager() {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(myRealm());
        return manager;
    }
    
    @Bean
    ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // 设置securityManager
        bean.setSecurityManager(securityManager());
        // 登录请求 默认为login.jsp
        bean.setLoginUrl("/login");
        bean.setSuccessUrl("/index");
        // 未授权页面
        bean.setUnauthorizedUrl("/unauthorizedurl");
        Map<String, String> map = new LinkedHashMap<>();
        //配置静态资源放行
        map.put("/static/**", "anon");
        //rememberMe 登录即可访问
        map.put("/", "user");
        map.put("/index", "user");
        //资源路径权限
        map.put("/user/add", "perms[user:add]");
        map.put("/user/update", "perms[user:update]");
        bean.setFilterChainDefinitionMap(map);
        return bean;
    }
}

Shiro 的配置主要配置 3 个 Bean :

  1. 首先需要提供一个 Realm 的实例。
  2. 需要配置一个 SecurityManager,在 SecurityManager 中配置 Realm。
  3. 配置一个 ShiroFilterFactoryBean ,在 ShiroFilterFactoryBean 中指定路径拦截规则等。
  4. 配置登录和测试接口。

其中,ShiroFilterFactoryBean 的配置稍微多一些,配置含义如下:

  • setSecurityManager 表示指定 SecurityManager。
  • setLoginUrl 表示指定登录页面。
  • setSuccessUrl 表示指定登录成功页面。
  • 接下来的 Map 中配置了路径拦截规则,注意,要有序。

SecurityManager是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。

1 第一次带jsessionid问题

 //解决Shiro第一次重定向url携带jsessionid问题
@Bean(name = "sessionManager")
public DefaultWebSessionManager mySessionManager() {
    DefaultWebSessionManager defaultSessionManager = new DefaultWebSessionManager();
    //设置成false
    defaultSessionManager.setSessionIdUrlRewritingEnabled(false);
    return defaultSessionManager;
}

将配置的bean注入到DefaultWebSecurityManager

2 配置RememberMe

@Bean("cookieManager")
public CookieRememberMeManager getCookieRememberMeManager(
    @Qualifier("simpleCookie") SimpleCookie simpleCookie) {
    CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
    rememberMeManager.setCookie(simpleCookie);
    // rememberMe cookie加密的密钥
    rememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
    return rememberMeManager;
}

@Bean(name = "simpleCookie")
public SimpleCookie getSimpleCookie() {
    SimpleCookie simpleCookie = new SimpleCookie();
    // 对应form表单checkbox的name=rememberMe
    simpleCookie.setName("rememberMe");
    // 使用HttpOnly cookie有助于减少某些类型的跨站点脚本攻击
    simpleCookie.setHttpOnly(true);
    simpleCookie.setMaxAge(3600);
    return simpleCookie;
}

将配置的bean注入到DefaultWebSecurityManager

3 配置授权信息Cache

// 用户授权信息Cache
@Bean(name = "memoryConstrainedCacheManager")
public MemoryConstrainedCacheManager cacheManager() {
    return new MemoryConstrainedCacheManager();
}

将配置的bean注入到DefaultWebSecurityManager

4 自定义密码匹配

@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
    //设置用于匹配密码的CredentialsMatcher
    HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
    matcher.setHashAlgorithmName(Sha512Hash.ALGORITHM_NAME);
    //加密次数
    matcher.setHashIterations(2);
    // 默认为16进制编码
    matcher.setStoredCredentialsHexEncoded(true);
   return matcher;
}

将配置的HashedCredentialsMatcher注入到自定义的myReam中

myRealm.setCredentialsMatcher(hashedCredentialsMatcher());

自定义myReam中doGetAuthenticationInfo方法返回设置

return new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()),getName());

密码匹配原理

  1. 在登录后台url中,我们拿到用户输入的用户名和密码,并组成一个token传给了shiro的login()方法。
  2. login()方法进行登录验证过程中,调用Realm对象的doGetAuthenticationInfo()方法,在这里根据token中的用户名在数据库中查找对应用户,用用户对象、数据库中存储的密码、密码盐和Realm对象名字构建一个认证信息对象SimpleAuthenticationInfo交给系统进行密码验证。
  3. 根据在配置文件中配置的密码匹配器,调用doCredentialsMatch()方法进行密码匹配。默认是SimpleCredentialsMatcher匹配器,他是以明文方式进行用户输入的密码和数据库中保存的密码进行匹配。
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
    Object tokenHashedCredentials = hashProvidedCredentials(token, info);
    Object accountCredentials = getCredentials(info);
    return equals(tokenHashedCredentials, accountCredentials);
}

在配置中使用的是HashedCredentialsMatcher匹配器,使用更安全的sha521哈希算法,指定了数据库中密码字段使用base64方式加密。
那么在创建用户或修改密码时怎么生成加密密码呢?利用shiro提供的simplehash()方法就可以,如下指定了sha521算法,密码字符串,密码盐和迭代次数(需要和config配置里的次数相同),最后对生成的哈希密码串进行base64编码。

new SimpleHash(Sha512Hash.ALGORITHM_NAME, password, ByteSource.Util.bytes(salt), hashIterations).toBase64

5 密码加密Utils

导入commons-lang依赖

<!--扩展工具包-->
<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.6</version>
</dependency>

ShiroUtils

package com.example.utils;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.shiro.crypto.hash.SimpleHash;

public class ShiroUtils {

    // PWD_SALT_LENGTH: 密码加密盐值长度
    public static final int PWD_SALT_LENGTH = 6;

    // PWD_ALGORITHM_NAME: 密码加密算法
    public static final String PWD_ALGORITHM_NAME = "SHA-512";


    // PWD_ALGORITHM_NAME: 密码加密次数
    public static final int PWD_HASH_ITERATIONS = 2;


    // 生成密码16进制编码
    public static String generatePwdHexEncrypt(String password, String salt) {
        SimpleHash hash =
                new SimpleHash(PWD_ALGORITHM_NAME, password, salt, PWD_HASH_ITERATIONS);
        return hash.toString();
    }

    // 生成密码64位编码
    public static String generatePwdBase64Encrypt(String password, String salt) {
        SimpleHash hash =
                new SimpleHash(PWD_ALGORITHM_NAME, password, salt, PWD_HASH_ITERATIONS);
        return hash.toBase64();
    }

    // 生成盐值
    public static String generateSalt() {
        return RandomStringUtils.randomAlphabetic(PWD_SALT_LENGTH);
    }

}

配置登录 Controller

    @RequestMapping("/login")
    public String login(@RequestParam("username") String username,
                        @RequestParam("password") String password,
                        String rememberMe, Model model) {

        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        if (rememberMe != null) {
            //设置记住我
            token.setRememberMe(true);
        }
        try {
            subject.login(token);
            return "redirect:/index";
        } catch (UnknownAccountException e) {
            model.addAttribute("msg", "用户名不存在!");
            return "/login";
        } catch (IncorrectCredentialsException e) {
            model.addAttribute("msg", "密码错误!");
            return "/login";
        }
    }

身份认证流程

流程如下:

  1. 首先调用 Subject.login(token) 进行登录,其会自动委托给 Security Manager,调用之前必须通过 SecurityUtils.setSecurityManager() 设置;
  2. SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
  3. Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
  4. Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
  5. Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。

shiro整合thymeleaf

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--shiro-thymeleaf整合-->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

在shiroConfig中配置

//shiro-thymeleaf整合
@Bean
public ShiroDialect getShiroDialect() {
    return new ShiroDialect();
}

示例:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<!--shiro:guest 判断是否为游客-->
<h3 shiro:guest>
    <a th:href="@{/toLogin}">登录</a>
</h3>
<h3 th:if="${user!=null}">
    <span th:text="${user.name}"></span>
    <a th:href="@{/toLogOut}">退出</a>
</h3>
<div>
    <!--判断是否有user:add权限-->
    <span shiro:haspermission="user:add"><a th:href="@{/user/add}">add</a> </span>
    <span shiro:haspermission="user:update"><a th:href="@{/user/update}">update</a></span>
</div>
</body>
</html>

Shiro Starter

1.shiro相关依赖

<!--shiro-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.6.0</version>
</dependency>

2.yaml配置

shiro-spring-boot-web-starter官方对配置进行了一系列的简化,并加入了一些自动配置项,所以我们要在yml中加入

spring:
  thymeleaf:
    cache: false
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shirodemo?useUnicoding=true&character=utf-8&useSSL=true
    username: root
    password: "000000"
mybatis:
  mapper-locations: mappers/*.xml
  type-aliases-package: com.example.entity
# shiro配置
shiro:
  enabled: true
  web:
    enabled: true
  unauthorizedUrl: /unauthorized
  loginUrl: /login
  successUrl: /index

全部配置(有些未生效)

键                                                   默认值     描述
shiro.enabled                                        true      启用Shiro的Spring模块
shiro.web.enabled                                    true      启用Shiro的Spring Web模块
shiro.annotations.enabled                            true      为Shiro的注释启用Spring支持
shiro.sessionManager.deleteInvalidSessions           true      从会话存储中删除无效会话
shiro.sessionManager.sessionIdCookieEnabled          true      启用会话ID到cookie,用于会话跟踪
shiro.sessionManager.sessionIdUrlRewritingEnabled    true      启用会话URL重写支持
shiro.userNativeSessionManager                       false     如果启用,Shiro将管理HTTP会话而不是容器
shiro.sessionManager.cookie.name                  JSESSIONID   会话cookie名称
shiro.sessionManager.cookie.maxAge                     -1      会话cookie最大年龄
shiro.sessionManager.cookie.domain                    空值      会话cookie域
shiro.sessionManager.cookie.path                      空值      会话cookie路径
shiro.sessionManager.cookie.secure                  false      会话cookie安全标志
shiro.rememberMeManager.cookie.name            rememberMe      RememberMe cookie名称
shiro.rememberMeManager.cookie.maxAge                一年       RememberMe cookie最大年龄
shiro.rememberMeManager.cookie.domain                空值       RememberMe cookie域名
shiro.rememberMeManager.cookie.path                  空值       RememberMe cookie路径
shiro.rememberMeManager.cookie.secure               false      RememberMe cookie安全标志
shiro.loginUrl                                 /login.jsp      未经身份验证的用户重定向到登录页面时使用的登录URL
shiro.successUrl                                       /      用户登录后的默认登录页面(如果在当前会话中找不到替代)
shiro.unauthorizedUrl                                空值      页面将用户重定向到未授权的位置(403页)

3.ShiroConfig配置

@Configuration
public class ShiroConfig {
    @Bean
    MyRealm myRealm() {
        return new MyRealm();
    }
    @Bean
    DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(myRealm());
        return manager;
    }
  @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition filterChainDefinition = new DefaultShiroFilterChainDefinition();
        Map<String, String> pathDefinitions = new LinkedHashMap<>();
        // 静态资源放行
        pathDefinitions.put("/static/**", "anon");
        pathDefinitions.put("favicon.ico", "anon");

        pathDefinitions.put("/findUser","authc");
        pathDefinitions.put("/user/add", "perms[user:add]");
        pathDefinitions.put("/user/update", "perms[user:update]");
        pathDefinitions.put("/user/delete/*", "perms[user:delete]");
        //认证 必须放在授权的代码下,否则授权的拦截无效!
        pathDefinitions.put("/user/**","authc");
        filterChainDefinition.addPathDefinitions(pathDefinitions);
        return filterChainDefinition;
}

注意:

  1. securityManagerDefaultWebSecurityManager
  2. 不再需要 ShiroFilterFactoryBean 实例了,替代它的是 ShiroFilterChainDefinition

参考资料:

https://www.cnblogs.com/telwanggs/p/10809536.html

https://blog.csdn.net/qq_41717874/article/details/84989988

原文地址:https://www.cnblogs.com/liulyuan/p/13916280.html