springboot整合shiro(超详细,你想要的都在这了)

Springboot整合Shiro 测试(该用户没有export功能,输入地址出现这个)修改ShiroConfig(前面代码弄过了)创建LoginFilter。 阅读详情

Springboot整合Shiro

文章目录


pom依赖

<dependencies>
        <!--thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.8.0</version>
        </dependency>
        <!--整合mybatis-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <!--整合druid数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>
        <!--log4j-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!--thymeleaf与shiro整合-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.1.0</version>
        </dependency>
        <!--导入hutool的依赖-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.5.7</version>
        </dependency>
        <!--shiro缓存,添加ehcache-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>

前端页面(thymeleaf整合shiro)

首页index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>首页</h1>
<h2 th:text="${msg}"></h2>
<div shiro:guest="">
    <a th:href="@{/toLogin}">登录</a>
</div>
<div shiro:hasPermission="user:add">
    <a th:href="@{/toAdd}">add</a>
</div>
<div shiro:hasPermission="user:update">
    <a th:href="@{/toUpdate}">update</a>
</div>
<div shiro:hasRole="vip1">
    <p>vip1</p>
</div>
<div shiro:hasRole="vip2">
    <p>vip2</p>
</div>

<div shiro:user>
    <a th:href="@{/buy}">记住我能看到,但认证才能用(比如支付功能)</a>
</div>

<div shiro:user="true">
    记住我或认证都能看到哦
</div>

<div shiro:user>
    <a th:href="@{/logout}">注销</a>
</div>
</body>
</html>

登录页login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登录</h1>
<p th:text="${msg}" style="color: red"></p>
<form action="/login" method="post">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="checkbox" name="rememberMe" value="1">记住我 <br>
    <input type="submit">
</form>

</body>
</html>

再写几个要跳转的页面,如add,update,noauth(不具有权限跳转到的页面),里面随便放点东西即可
在这里插入图片描述

thymeleaf中shiro标签解释

<!-- 当前用户是否为“游客”,如果是未认证,也未记住的用户,那么就显示该 p 标签中的内容 -->
<p shiro:guest="">Please <a href="login.html">login</a></p>


<!-- 未认证的用户但已记住,与下面的 authenticated 标签相对应。与 guest 标签的区别是,该标签包含已记住用户。 -->
<p shiro:notAuthenticated="">
    Please <a href="login.html">login</a> in order to update your credit card information.
</p>



<!-- 认证通过或已记住的用户,则显示该 p 标签中的内容 -->
<p shiro:user="">
    Welcome back John! Not John? Click <a href="login.html">here</a> to login.
</p>


<!-- 已认证通过,但未记住的用户。这是与 user 标签的区别所在。 -->
<p shiro:authenticated="">
    Hello, <span shiro:principal=""></span>, how are you today?
</p>
<a shiro:authenticated="" href="updateAccount.html">Update your contact information</a>


<!-- 输出当前用户信息,通常为登录帐号信息 -->
<p>Hello, <shiro:principal />, how are you today?</p>


<!-- 验证当前用户是否具有该 admin 角色,若拥有,则显示 a 标签的内容 -->
<a shiro:hasRole="admin" href="admin.html">Administer the system</a>


<!-- 与 hasRole 标签逻辑相反,当用户不属于该 developer 角色时显示 -->
<p shiro:lacksRole="developer">
    Sorry, you are not allowed to developer the system.
</p>


<!-- 验证当前用户是否同时具有以下所有角色 -->
<p shiro:hasAllRoles="developer, product">
    You are a developer and a admin.
</p>


<!-- 验证当前用户是否具于以下任意一个角色 -->
<p shiro:hasAnyRoles="admin, vip, developer">
    You are a admin, vip, or developer.
</p>


<!-- 验证当前用户是否拥有 update 权限 -->
<a shiro:hasPermission="update" href="createUser.html">添加用户</a>


<!-- 与 hasPermission 标签逻辑相反,当前用户不拥有指定权限 delete -->
<p shiro:lacksPermission="delete">
    Sorry, you are not allowed to delete user accounts.
</p>


<!-- 验证当前用户是否同时拥有以下所有权限 -->
<p shiro:hasAllPermissions="add, update">
    You can see or add users.
</p>


<!-- 验证当前用户是否拥有以下任意一个权限 -->
<p shiro:hasAnyPermissions="add, update, delete">
    You can see or delete users.
</p>

数据库(整合mybatis)

在这里插入图片描述

application.yml

spring:
  datasource:
    username : root
    password: 123456
    url : jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource # 自定义数据源

    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

mybatis:
  type-aliases-package: com.govd.pojo
  mapper-locations: classpath:mapper/*.xml

log4j配置文件log4j.properties

log4j.rootLogger=INFO, 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

# General Apache libraries
log4j.logger.org.apache=WARN

# Spring
log4j.logger.org.springframework=WARN

# Default Shiro logging
log4j.logger.org.apache.shiro=INFO

# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

编写pojo,这里记得一定要序列化
在这里插入图片描述

druid配置

@Configuration
public class DruidConfig {
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource DruidDataSource(){
        return new DruidDataSource();
    }
}

UserMapper

@Mapper
@Repository
public interface UserMapper {
    public User queryUserByName(String name);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.govd.mapper.UserMapper">
    <select id="queryUserByName" resultType="user">
        select * from user where name=#{name}
    </select>
</mapper>

之后编写对应的service即可


理解shiro的几个组成部分

在这里插入图片描述

●subject: 应用代码直接交互的对象是Subject, 也就是说Shiro的对外API核心就是Subject, Subject代表了当前的用户,这个用户不-定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等,与Subject的所有交互都会委托给SecurityManager; Subject其实是一一个门面, SecurityManageer 才是
实际的执行者
●SecurityManager: 安全管理器,即所有与安全有关的操作都会与SercurityManager交互, 并且它管理着所有的Subject,可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC的DispatcherServlet的角色
●Realm: Shiro从Realm获取安全数据 (如用户,角色,权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较,来确定用户的身份是否合法;也需要从Realm得到用户相应的角色、权限,进行验证用户的操作是否能够进行,可以把Realm看DataSource;


编写Shiro配置类(shiroConfig.class)

思路:ShiroFilterFactoryBean会拦截前端请求交给DefaultWebSecurityManager,再交给MyRealm进行认证和授权处理
主要编写MyRealm、DefaultWebSecurityManager、ShiroFilterFactoryBean三个bean对象,
三个对象从前往后写。

ShiroFilterFactoryBean
内置过滤器参数说明:

anon: 无需认证即可访问
authc: 必须认证才能用
user: 必须拥有 “记住我” 功能才能用
perms: 拥有对某个资源的权限才能用
role: 拥有某个角色权限才能访问

设置登录页面

setLoginUrl(“/toLogin”);

注销登录用户的两种方式
第一种,在controller中处理

@RequestMapping("/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "index";
    }

第二种,ShiroFilterFactoryBean中添加注销过滤器
在这里插入图片描述

设置没有权限时跳转到的页面

setUnauthorizedUrl(“/noauth”)

在这里插入图片描述

完整ShiroFilterFactoryBean配置

	@Bean
    public ShiroFilterFactoryBean bean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        //添加shiro的内置过滤器
        /*
        anon: 无需认证即可访问
        authc: 必须认证才能用
        user: 必须拥有 “记住我” 功能才能用
        perms: 拥有对某个资源的权限才能用
        role: 拥有某个角色权限才能访问
        */

        Map<String,String> filterMap=new HashMap<>();

        //登陆后授权,正常情况下没有授权会跳转到未授权页面
        filterMap.put("/toAdd","perms[user:add]");
        filterMap.put("/toUpdate","perms[user:update]");
        //设置注销过滤器
        filterMap.put("/logout","logout");

        /**
         *    /** 匹配所有的路径
         *   通过Map集合组成了一个拦截器链 ,自顶向下过滤,一旦匹配,则不再执行下面的过滤
         *   如果下面的定义与上面冲突,那按照了谁先定义谁说了算
         *   所以/** 一定要配置在最后
         *   这里是否要对所有路径进行认证视情况而定,因为一些路由跳转可能在没登陆出现导致出错,所以这里考虑清楚
         **/
        //filterMap.put("/**", "authc");
        
		// 将拦截器链设置到shiro中
        bean.setFilterChainDefinitionMap(filterMap);
        //设置登录页面
        bean.setLoginUrl("/toLogin");
		// 登录成功后要跳转的链接
        //shiroFilterFactoryBean.setSuccessUrl("/index");

        //设置未授权页面
        bean.setUnauthorizedUrl("/noauth");

        return bean;
    }

编写DefaultWebSecurityManager

	@Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(@Qualifier("myRealm") MyRealm myRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        return securityManager;
    }

编写MyRealm类

public class MyRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;

    //执行授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //认证之后,如果前端shiro标签中有出现需要权限的标签,或者过滤器中某个链接需要权限,就会进行认证
        System.out.println("执行了授权");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获得当前subject
        Subject subject = SecurityUtils.getSubject();
        //获得当前的principal,也就是认证完后我们放入的信息
        User currentUser = (User) subject.getPrincipal();
        //添加权限
        info.addStringPermission(currentUser.getPerms());
        //添加角色
        info.addRole(currentUser.getRole());

        return info;
    }

    //执行认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken Token) throws AuthenticationException {
        System.out.println("执行了认证");
        UsernamePasswordToken token = (UsernamePasswordToken) Token;
        //从数据库中查询该用户
        User user = userService.queryUserByName(token.getUsername());
        //如果不存在该用户,返回一个空错误,前端也可以相应显示提示
        if(user==null){
            return null;
        }
        //第一个参数为principal;第二个参数为从数据库中查出的用于验证的密码,shiro中密码验证不需要我们自己去做;第三个参数为realmName
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");
    }
}

将其在shiroConfig中注册成bean

	@Bean
    public MyRealm myRealm(){
        MyRealm myShiroRealm = new MyRealm();
        return myShiroRealm;
    }

controller

编写routerController

@Controller
public class routerController {
    //跳到首页
    @RequestMapping({"/", "/index"})
    public String toIndex(Model model){
        model.addAttribute("msg","hello shiro");
        return "index";
    }
    //跳到登录页
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }

    @RequestMapping("/toAdd")
    public String toAdd(){
        return "add";
    }

    @RequestMapping("/toUpdate")
    public String toUpdate(){
        return "update";
    }

    //跳到未授权页面
    @RequestMapping("/noauth")
    public String tonoauth(){
        return "noauth";
    }

    //用于测试记住我和认证的区别
    @RequestMapping("/buy")
    public String buy(){
        Subject subject = SecurityUtils.getSubject();
        //只有认证后才能访问,如果只是记住我则需要先登录
        if(!subject.isAuthenticated()){
            return "redirect:/toLogin";
        }
        return "add";
    }

    //登录认证
    @RequestMapping("/login")
    public String login(String username,String password,Integer rememberMe,Model model){
        Subject subject = SecurityUtils.getSubject();
        //令牌
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        if(rememberMe!=null&&rememberMe==1){
            token.setRememberMe(true);
        }

        try {
            //登录认证
            subject.login(token);
            return "index";
        }catch (UnknownAccountException e){ //返回null就会进入这里
            model.addAttribute("msg","用户名不存在!");
            return "login";
        }catch (IncorrectCredentialsException e){ //密码错误就会进入这里
            model.addAttribute("msg","密码错误!");
            return "login";
        }

    }

    //注销
    @RequestMapping("/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "index";
    }
    
//    第二种注销方式,通过过滤器链注销,这里就直接返回首页即可
//    @RequestMapping("/logout")
//    public String logout(){
//        return "index";
//    }

}

记住我

记住我功能是要在用户登录成功以后,假如关闭浏览器,下次再访问系统资源(例如首页doIndexUI)时,无需再执行登录操作。
在这里插入图片描述
前端:
在这里插入图片描述
在Controller中的login方法中基于是否选中记住我,设置token的setRememberMe方法。
在这里插入图片描述
在ShiroConfig配置类中添加记住我配置,关键代码如下:

@Bean(name = "rememberMeManager")
    public CookieRememberMeManager rememberMeManager(){
        //cookie管理器
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        //cookie的名字
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //设置有效期时间30天
        simpleCookie.setMaxAge(259200);
        cookieRememberMeManager.setCookie(simpleCookie);
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("6ZmI6I2j5Y+R5aSn5ZOlAA=="));

        return cookieRememberMeManager;
    }

这里要设置cookie加密的密钥是为了给他指定一种加密方式,否则会出现关闭浏览器后再打开记住我失效,得刷新网页后才正常,因为两次加密后不一样,就像拿密钥b去匹配上次的密钥a,会导致失效

这里还有一个问题,困扰了我好久,那就是用了记住我之后关闭浏览器再次打开,第一次访问的时候总会出现错误页面,刷新之后才正常,而注意到url上总有一个jsession=******** 的东西,查了好久终于解决

解决Shiro第一次重定向url携带jsessionid问题
Shiro在进行第一次重定向时会在url后携带jsessionid,导致访问400。
解决办法:

创建一个DefaultWebSessionManager类实例,并将它的sessionIdUrlRewritingEnabled属性设置成false
再在DefaultWebSecurityManager类中将上面的实例设置为它的SessionManager

//创建DefaultWebSessionManager类
@Bean
public DefaultWebSessionManager mySessionManager(){
    DefaultWebSessionManager defaultSessionManager = new DefaultWebSessionManager();
    //将sessionIdUrlRewritingEnabled属性设置成false
    defaultSessionManager.setSessionIdUrlRewritingEnabled(false);
    return defaultSessionManager;
}

修改修改securityManager的配置,为securityManager注入rememberManager对象和defaultSessionManager对象

@Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(@Qualifier("myRealm") MyRealm myRealm, @Qualifier("rememberMeManager") CookieRememberMeManager rememberMeManager, @Qualifier("mySessionManager") DefaultWebSessionManager webSessionManager, ){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        securityManager.setRememberMeManager(rememberMeManager);
        securityManager.setSessionManager(webSessionManager);
      
        return securityManager;
    }

开启缓存

缓存是提供性能的重要手段。缓存适合那些经常不变动的数据,比如系统中用户的信息和权限不会经常改变,特别适合缓存起来供下次使用。这样减少了系统查询数据库的次数,提升了性能。shiro自身不实现缓存,而是提供缓存接口,让其他第三方实现,经常使用ehcache缓存。

有两种缓存方式,但较多使用ehcache

在resources下面新建config文件夹,并创建ehcache-shiro.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="es">

    <!--
        缓存对象存放路径
        java.io.tmpdir:默认的临时文件存放路径。
        user.home:用户的主目录。
        user.dir:用户的当前工作目录,即当前程序所对应的工作路径。
        其它通过命令行指定的系统属性,如“java –DdiskStore.path=D:\abc ……”。
    -->
    <diskStore path="java.io.tmpdir"/>

    <!--

       name:缓存名称。
       maxElementsOnDisk:硬盘最大缓存个数。0表示不限制
       maxEntriesLocalHeap:指定允许在内存中存放元素的最大数量,0表示不限制。
       maxBytesLocalDisk:指定当前缓存能够使用的硬盘的最大字节数,其值可以是数字加单位,单位可以是K、M或者G,不区分大小写,
                          如:30G。当在CacheManager级别指定了该属性后,Cache级别也可以用百分比来表示,
                          如:60%,表示最多使用CacheManager级别指定硬盘容量的60%。该属性也可以在运行期指定。当指定了该属性后会隐式的使当前Cache的overflowToDisk为true。
       maxEntriesInCache:指定缓存中允许存放元素的最大数量。这个属性也可以在运行期动态修改。但是这个属性只对Terracotta分布式缓存有用。
       maxBytesLocalHeap:指定当前缓存能够使用的堆内存的最大字节数,其值的设置规则跟maxBytesLocalDisk是一样的。
       maxBytesLocalOffHeap:指定当前Cache允许使用的非堆内存的最大字节数。当指定了该属性后,会使当前Cache的overflowToOffHeap的值变为true,
                             如果我们需要关闭overflowToOffHeap,那么我们需要显示的指定overflowToOffHeap的值为false。
       overflowToDisk:boolean类型,默认为false。当内存里面的缓存已经达到预设的上限时是否允许将按驱除策略驱除的元素保存在硬盘上,默认是LRU(最近最少使用)。
                      当指定为false的时候表示缓存信息不会保存到磁盘上,只会保存在内存中。
                      该属性现在已经废弃,推荐使用cache元素的子元素persistence来代替,如:<persistence strategy=”localTempSwap”/>。
       diskSpoolBufferSizeMB:当往磁盘上写入缓存信息时缓冲区的大小,单位是MB,默认是30。
       overflowToOffHeap:boolean类型,默认为false。表示是否允许Cache使用非堆内存进行存储,非堆内存是不受Java GC影响的。该属性只对企业版Ehcache有用。
       copyOnRead:当指定该属性为true时,我们在从Cache中读数据时取到的是Cache中对应元素的一个copy副本,而不是对应的一个引用。默认为false。
       copyOnWrite:当指定该属性为true时,我们在往Cache中写入数据时用的是原对象的一个copy副本,而不是对应的一个引用。默认为false。
       timeToIdleSeconds:单位是秒,表示一个元素所允许闲置的最大时间,也就是说一个元素在不被请求的情况下允许在缓存中待的最大时间。默认是0,表示不限制。
       timeToLiveSeconds:单位是秒,表示无论一个元素闲置与否,其允许在Cache中存在的最大时间。默认是0,表示不限制。
       eternal:boolean类型,表示是否永恒,默认为false。如果设为true,将忽略timeToIdleSeconds和timeToLiveSeconds,Cache内的元素永远都不会过期,也就不会因为元素的过期而被清除了。
       diskExpiryThreadIntervalSeconds :单位是秒,表示多久检查元素是否过期的线程多久运行一次,默认是120秒。
       clearOnFlush:boolean类型。表示在调用Cache的flush方法时是否要清空MemoryStore。默认为true。
       diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
       maxElementsInMemory:缓存最大数目
       memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
            memoryStoreEvictionPolicy:
               Ehcache的三种清空策略;
               FIFO,first in first out,这个是大家最熟的,先进先出。
               LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
               LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
    -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="0"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
    />

    <!-- 授权缓存 -->
    <cache name="authorizationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>

    <!-- 认证缓存 -->
    <cache name="authenticationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>

</ehcache>

在shiroConfig中添加bean

	//  缓存配置
    //shiro自带的MemoryConstrainedCacheManager作缓存
    // 但是只能用于本机,在集群时就无法使用,需要使用ehcache
    @Bean(name = "cacheManager")
    public CacheManager cacheManager() {
        MemoryConstrainedCacheManager cacheManager=new MemoryConstrainedCacheManager();//使用内存缓存
        return cacheManager;
    }

    //配置ehcache,推荐使用
    @Bean(name = "ehCacheManager")
    public EhCacheManager ehCacheManager(){
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
        return ehCacheManager;
    }

修改myrealm

	@Bean
    public MyRealm myRealm(){
        MyRealm myShiroRealm = new MyRealm();
        myShiroRealm.setCachingEnabled(true);
        //启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
        myShiroRealm.setAuthenticationCachingEnabled(true);
        //缓存AuthenticationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
        myShiroRealm.setAuthenticationCacheName("authenticationCache");
        //启用授权缓存,即缓存AuthorizationInfo信息,默认false
        myShiroRealm.setAuthorizationCachingEnabled(true);
        //缓存AuthorizationInfo信息的缓存名称  在ehcache-shiro.xml中有对应缓存的配置
        myShiroRealm.setAuthorizationCacheName("authorizationCache");
        //myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return myShiroRealm;

    }

修改securityManager

 @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(@Qualifier("myRealm") MyRealm myRealm, @Qualifier("rememberMeManager") CookieRememberMeManager rememberMeManager, @Qualifier("mySessionManager") DefaultWebSessionManager webSessionManager, @Qualifier("ehCacheManager")EhCacheManager ehCacheManager){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        securityManager.setRememberMeManager(rememberMeManager);
        securityManager.setSessionManager(webSessionManager);
        //设置缓存管理
        //第一种缓存
        //securityManager.setCacheManager(cacheManager);
       //ehcache缓存,推荐
        securityManager.setCacheManager(ehCacheManager);

        return securityManager;
    }

修改MyRealm(可改可不改,改了只是可以更加自定义化)

public class MyRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;

    //执行授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //认证之后,如果前端shiro标签中有出现需要权限的标签,或者过滤器中某个链接需要权限,就会进行认证
        System.out.println("执行了授权");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获得当前subject
        Subject subject = SecurityUtils.getSubject();
        //获得当前的principal,也就是认证完后我们放入的信息
        User currentUser = (User) subject.getPrincipal();
        //添加权限
        info.addStringPermission(currentUser.getPerms());
        //添加角色
        info.addRole(currentUser.getRole());

        return info;
    }

    //执行认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken Token) throws AuthenticationException {
        System.out.println("执行了认证");
        UsernamePasswordToken token = (UsernamePasswordToken) Token;
        //从数据库中查询该用户
        User user = userService.queryUserByName(token.getUsername());
        //如果不存在该用户,返回一个空错误,前端也可以相应显示提示
        if(user==null){
            return null;
        }
        //第一个参数为principal;第二个参数为从数据库中查出的用于验证的密码,shiro中密码验证不需要我们自己去做;第三个参数为realmName
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");
    }

    /**
     * 重写方法,清除当前用户的的 授权缓存
     * @param principals
     */
    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    /**
     * 重写方法,清除当前用户的 认证缓存
     * @param principals
     */
    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

    /**
     * 自定义方法:清除所有 授权缓存
     */
    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }

    /**
     * 自定义方法:清除所有 认证缓存
     */
    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }

    /**
     * 自定义方法:清除所有的  认证缓存  和 授权缓存
     */
    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }
}

现在有了缓存,认证之后关于权限的授权就只需要走一次方法,而不需要频繁的调用,提高了性能


拓展功能

shiroConfig还可以自定义很多其他功能,思路就是创建对应功能的bean,然后相应修改securityManager和myrealm即可。

	/**
     * 密码匹配凭证管理器
     *
     * @return
     */
    @Bean(name = "hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        log.info("hashedCredentialsMatcher()");
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

        hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(1024);// 散列的次数,比如散列两次,相当于md5(md5(""));

        return hashedCredentialsMatcher;
    }
    
	 /**
     * 开启shiro aop注解支持
     * 使用代理方式;所以需要开启代码支持
     * @param securityManager
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * 开启cglib代理
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }
	
	//在用spring管理我们的类的时候有时候希望有些属性值是来源于一些配置文件,系统属性,或者一些方法调用的结果,
    // 对于前两种使用方式可以使用spring的PropertyPlaceholderConfigurer类来注入,
    // 对于后一种则可以使用org.springframework.beans.factory.config.MethodInvokingFactoryBean类来生成需要注入的bean的属性。
    // 通过MethodInvokingFactory Bean类,可注入方法返回值。
    // MethodInvokingFactoryBean用来获得某个方法的返回值,该方法既可以是静态方法,也可以是实例方法。
    // 该方法的返回值可以注入bean实例属性,也可以直接定义成bean实例
    //可查看http://blog.sina.com.cn/s/blog_72ef7bea0102wa0v.html
    /**
     * 让某个实例的某个方法的返回值注入为Bean的实例
     * Spring静态注入
     * @param myShiroRealm
     * @param rememberMeManager
     * @param ehCacheManager
     * @return
     */
    @Bean(name = "methodInvokingFactoryBean")
    public MethodInvokingFactoryBean methodInvokingFactoryBean(
            @Qualifier("myShiroRealm") MyShiroRealm myShiroRealm,
            @Qualifier("rememberMeManager") CookieRememberMeManager rememberMeManager,
            @Qualifier("ehCacheManager")EhCacheManager ehCacheManager){
        MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
        factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        factoryBean.setArguments(new Object[]{securityManager(myShiroRealm, rememberMeManager,ehCacheManager)});
        return factoryBean;
    }

如果自定义了密码加密验证,则修改myRealm
注意这里如果用了加密验证,则数据库一开始存的就是加密后的密码,然后也要对前端传过来的数据进行加密处理后再进行匹配验证

 @Bean(name = "myRealm")
    public MyRealm myRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher hashedCredentialsMatcher){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCachingEnabled(true);
        //启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
        myShiroRealm.setAuthenticationCachingEnabled(true);
        //缓存AuthenticationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
        myShiroRealm.setAuthenticationCacheName("authenticationCache");
        //启用授权缓存,即缓存AuthorizationInfo信息,默认false
        myShiroRealm.setAuthorizationCachingEnabled(true);
        //缓存AuthorizationInfo信息的缓存名称  在ehcache-shiro.xml中有对应缓存的配置
        myShiroRealm.setAuthorizationCacheName("authorizationCache");
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return new MyShiroRealm();
    }
SpringBoot整合Shiro(最详细) pom.xml 2.3 创建前端页面 在webapp文件夹中创建index.jsp和login.jsp index.jsp login.jsp 2.4 配置视图信息 application.properties 2.5 解决IDEA冲突问题 JSP 与IDEA 与SpringBoot存在一定的不兼容,修改此配置即可解决 pom.xml 3.2 自定义Realm 在shiro文件夹下创建realm文件夹 3.3 Shiro配置 在config文件夹中创建ShiroConfig.java 3.4 启动测试 输入 阅读详情

相关推荐

Shiro教程(二):Springboot整合Shiro全网最全教程

Apache Shiro是一个Java的安全权限框架。Shiro可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境。- Shiro可以完成认证、授权、加密、会话管理、Web集成、缓存等。这里是SpringBoot整合Shiro最完整最详细教程。

WwLK123的博客 2098

BATMAN-ADV协议在Mesh自组网中的拓扑发现与动态绘制

本文深入解析了BATMAN-ADV协议在Mesh自组网中的拓扑发现与动态绘制技术。通过分析协议基础、邻居发现机制和动态拓扑绘制实现,展示了其在应急通信和工业物联网中的实际应用与性能优化策略,为Mesh网络部署提供实用指南。

weixin_33701294的博客 513

Linux:安装Debian最新10.x操作系统(超详细

安装Debian最新10.x操作系统安装Debian最新10.x操作系统1.下载Debian镜像2. 镜像安装引导启动页面,选择图形化安装:选择语言、时区,选中后点击continue:选择键盘模式:配置Hostname主机名,设置Root密码创建普通用户名和密码,进入磁盘分区步骤,选择“所有文件放在同一个分区”确认分区信息磁盘配置完,进入软件安装模块:选择源:进入软件包配置,等待配置结束根据需要选择软件包安装3. 安装完成重启后登录使用 安装Debian最新10.x操作系统 1.下载Debian镜像 下载D

HowieXue 薛永浩的博客 3万+

企业员工角色权限管理平台(SpringBoot2.0+Mybatis+Shiro+Vue)

课程简介:历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring BootSpring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:

Springboot整合Shiro框架入门

Springboot整合Shiro框架入门 1、快速开始demo 2、Springboot集成Shiro 2.1、环境搭建 2.2、shiro的拦截 2.3、shiro的认证 2.4、整合mybatis 2.5、shiro进行授权 2.6、shiro整合thymeleaf 3、小结 1、快速开始demo 来到shiro的官网:shiro官方网站: 下载官方在git仓库上提供的源码,下载后解压: 官方提供了在多种场景下使用的demo,等一下会用到sample里面的东西。下面直接按官方推荐

web15285868498的博客 8307

springboot整合shiro和redis(超详细案例演示)

Spring Boot、Redis和Shiro整合在一起具有多个优势。首先,通过与Spring Boot的集成,可以简化开发过程,利用其自动配置和约定优于配置的原则。其次,Redis作为高性能的缓存和存储系统,可以提供快速的数据访问和响应时间,通过与Spring BootShiro整合,可以实现更高效的会话管理和身份验证。此外,通过将Shiro的会话存储在Redis中,可以实现分布式会话管理和高可用性,并支持共享会话和无状态应用程序架构。

m0_57232638的博客 5127

SpringBoot整合Shiro(超详细,适合新手学习,附有源码)

shiro是什么呢?Shiro是一个功能强大,简单易用的 Java 安全框架,提供了用户认证、授权、加密、会话管理、缓存等功能。和目前另一款Java安全框架Spring Security相比,Shiro更加轻便,易于上手。各功能点介绍Authentication:用户身份认证/登录,即验证用户是不是合法用户Authorization:用户权限验证,即验证用户是否拥有某个角色,是否拥有某个权限。

lianaozhe的博客 1638

springboot整合shiro安全框架(超详细)新手必看

自己第一次搞shiro的时候看了很多博客,做的时候还是一知半解的,也不能说别人的写的不好,可能自己领悟的比较慢,有些细节需要自己去思考,以至于花费的时间有点多,但也不都是坏处,这样有助于我们理解的更深刻。这篇文章希望可以帮助大家更快的完成spring集成shiro 工具 IntelliJ IDEA 关于怎么新建一个springboot项目我这里就不赘述了,不懂的小伙伴可以先看一下这篇博客ide...

cyy56的博客 1253

SpringBootshiro整合项目实战四(图解超详细

承接上一篇博客:https://blog.csdn.net/qq_41115379/article/details/113088489 首先要说的是,这个项目实战是参考自黑马,可以去对应视频看一看:https://www.bilibili.com/video/BV1ct411x7CN 首先在pom里导入对应的依赖 <!-- druid --> <dependency> <groupId>com.alibaba</groupId>

wzw的博客 385

SpringBootshiro整合项目实战二(图解超详细

承接上一篇博客:https://blog.csdn.net/qq_41115379/article/details/112978080 首先要说的是,这个项目实战是参考自黑马,可以去对应视频看一看:https://www.bilibili.com/video/BV1ct411x7CN 接下来按照这几个步骤 首先要了解,shiro有着几个核心api subject:用户主体(把操作交给SecurityManager) SecurityManager:安全管理器(关联Realm) Realm:shiro连接

wzw的博客 420

springBoot整合Shiro(详细教程分析)

🚩1.1 什么是ShiroShiro是一个开源的Java安全框架,它提供了身份认证、授权、加密和会话管理等功能,可以帮助我们快速地构建安全可靠的应用程序。🚩1.2 为什么要用Shiro在开发Web应用程序时,安全性是一个非常重要的问题。使用Shiro可以帮助我们快速地构建安全可靠的应用程序,而且Shiro的使用非常灵活,可以根据我们的需求进行定制。

ggjklncffd的博客 6363

SpringBootShiro整合

本文章介绍了Spring bootshiro与thymeleaf、mybatis的整合过程,Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理

fangliangke的博客 6513

Spring Boot学习之Shiro

Apache Shiro 是一个Java 的安全(权限)框架。Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等。Shiro官网。

缘友一世的博客 2684

Springboot整合shiro

是一款主流的Java安全框架,不依赖任何容器,可以运行在Java SE和Java EE项目中,它的主要作用是对访问系统的用户进行身份认证、授权、会话管理、加密等操作。Shiro 就是用来解决安全管理的系统化框架。Shiro是基于session的身份认证和访问控制框架。RBAC是Role-Based Access Control(基于角色的访问控制)的缩写。它是一种广泛应用于安全管理中的访问控制模型。RBAC模型通过授予用户不同的角色,并将角色与权限进行关联,来管理对资源的访问。

chao的博客 2781

Shiro安全框架详解及springboot使用示例

目录一、认识Shiro1、什么是Shiro?2、有哪些功能?3、Shiro架构(外部)4、Shiro架构(内部) 一、认识Shiro 1、什么是Shiro? Apache Shiro 是一个Java 的安全(权限)框架。 Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环 境。 Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等。 2、有哪些功能? Authentication:身份认证、登录,验证用户是不是拥有相应的身份; Aut

weixin_46822367的博客 4357

inno setup自定义卸载程序和美化

也就是DisplayName,因为我们在第一步的时候,将卸载程序名称也写入了注册表,所以,我们直接从注册表读DisplayName字段即可。1.要实现inno setup自定义卸载程序,首先需要关闭自带的卸载程序。在[Setup]的字段关闭卸载程序步骤和页面。2.删除应用程序目录里能够删除的并且返回进度。3.自定义卸载程序相关的注册表信息。4.删除启动菜单栏和guid注册表。2.自定义快捷方式和程序菜单。

cs821984831的博客 2324

winfix.rar

修复系统故障专用,内含winfix和dneupdate64.msi修复系统故障专用,内含winfix和dneupdate64.msi修复系统故障专用,内含winfix和dneupdate64.msi

上一篇: Seaborn的简述
下一篇: 卧槽,2行代码,让接口性能提升10倍
澎湖Java架构师
博客等级 码龄6年 1万+粉丝 647原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值