shiro(二):springboot整合shiro

news2024/11/20 11:51:37

1. 整合思路

在这里插入图片描述

2. 加入jsp相关配置方便测试

2.1 加入依赖:

<!--引入JSP解析依赖-->
<dependency>
     <groupId>org.apache.tomcat.embed</groupId>
     <artifactId>tomcat-embed-jasper</artifactId>
     </dependency>
<dependency>
     <groupId>jstl</groupId>
     <artifactId>jstl</artifactId>
     <version>1.2</version>
</dependency>

2.2 application.properties 文件

server.servlet.context-path=/shiro
spring.application.name=shiro

spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

2.3 添加启动项配置

在这里插入图片描述

2.4 jsp页面代码:

index.jsp

<%@page contentType="text/html;utf-8" pageEncoding="utf-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<%--    受限资源--%>
<h1>系统主页</h1>
<%--由于A标签只能发送get请求,如果需要post请求需要使用函数或者其他方式,此处直接get提交,注意项目中尽量post --%>
<a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
<ul>
    <li><a href="#">用户管理</a></li>
    <li><a href="#">商品管理</a></li>
    <li><a href="#">订单管理</a></li>
    <li><a href="#">物流管理</a></li>
</ul>
</body>
</html>

login.jsp

<%@page contentType="text/html;utf-8" pageEncoding="utf-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<h1>登录界面</h1>
<form action="${pageContext.request.contextPath}/user/login" method="post">
    用户名:<input type="text" name="username" > <br/>
    密码  : <input type="text" name="password"> <br>
    <input type="submit" value="登录">
</form>
</body>
</html>

2.5 目录结构:

在这里插入图片描述

2.5 查看效果

在这里插入图片描述

3. 关联springboot配置简单使用

3.1 引入依赖

<!--引入shiro整合Springboot依赖-->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring-boot-starter</artifactId>
  <version>1.5.3</version>
</dependency>

3.2 配置类编写:

@Configuration
public class ShiroConfig {
    //1.创建shiroFilter  //负责拦截所有请求
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //给filter设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);

        //配置系统受限资源
        //配置系统公共资源
        Map<String,String> map = new HashMap<String,String>();

        map.put("/index.jsp","authc");//authc 请求这个资源需要认证和授权

        //默认认证界面路径---当认证不通过时跳转
        shiroFilterFactoryBean.setLoginUrl("/login.jsp");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

        return shiroFilterFactoryBean;
    }

    //2.创建安全管理器
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        //给安全管理器设置
        defaultWebSecurityManager.setRealm(realm);

        return defaultWebSecurityManager;
    }

    //3.创建自定义realm
    // 此处realm中先不加内容直接返回null
    @Bean
    public Realm getRealm(){
        CustomRealm1 customerRealm = new CustomRealm1();

        return customerRealm;
    }
}

3.3 启动错误解决:

3.3.1 报错1:
Parameter 0 of method getDefaultWebSecurityManager in com.yhx.toali.shiroStudy.withSpringboot.ShiroConfig required a single bean, but 2 were found:
- getRealm: defined by method 'getRealm' in class path resource [com/yhx/toali/shiroStudy/withSpringboot/ShiroConfig.class]
- iniClasspathRealm: defined by method 'iniClasspathRealm' in class path resource [org/apache/shiro/spring/boot/autoconfigure/ShiroAutoConfiguration.class]

这是因为之前在测试的时候编写了shiro.ini,进入到ShiroAutoConfiguration这个类查看,可以看到根据shiro.ini生成了一个realm:

 @Bean
 @ConditionalOnResource(
     resources = {"classpath:shiro.ini"}
 )
 protected Realm iniClasspathRealm() {
     return this.iniRealmFromLocation("classpath:shiro.ini");
 }
3.3.2 报错2:
Description:

Method filterShiroFilterRegistrationBean in org.apache.shiro.spring.config.web.autoconfigure.ShiroWebFilterConfiguration required a bean named 'shiroFilterFactoryBean' that could not be found.


Action:

Consider defining a bean named 'shiroFilterFactoryBean' in your configuration.

设置bean的名称:@Bean("shiroFilterFactoryBean")

3.4 启动项目查看index.jsp

可以看到访问index.jsp自动跳到了login.jsp
在这里插入图片描述

4. 常见过滤器

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

其中主要使用的是anon和authc
在这里插入图片描述

5. 认证的登陆和退出实现

5.1 controller方法编写:

@Controller
@RequestMapping("user")
public class UserController {
    /**
     * 退出登录
     */
    @PostMapping("logout")
    public String logout() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();//退出用户
        return "redirect:/login.jsp";
    }

    /**
     * 用来处理身份认证
     * @param username
     * @param password
     * @return
     */
    @PostMapping("login")
    public String login(String username, String password) {
        try {
            //获取主体对象
            Subject subject = SecurityUtils.getSubject();
            // 在认证过程中使用subject.login进行认证
            subject.login(new UsernamePasswordToken(username, password));
            return "redirect:/index.jsp";
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误!");
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("密码错误!");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }

        return "redirect:/login.jsp";
    }
}

5.2 jsp页面事件触发

上面jsp页面代码已有,此处略

5.3 修改自定义realm实现登陆验证

//自定义realm
public class CustomRealm1 extends AuthorizingRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("=============");
        //从传过来的token获取到的用户名
        String principal = (String) token.getPrincipal();
        System.out.println("用户名"+principal);

        //假设是从数据库获得的 用户名,密码
        String password_db="123";
        String username_db="zhangsan";

        if (username_db.equals(principal)){
//            SimpleAuthenticationInfo simpleAuthenticationInfo =
            return new SimpleAuthenticationInfo(principal,password_db, this.getName());
        }

        return null;
    }
}

5.4 修改shiroConfig类中的getShiroFilterFactoryBean()方法进行资源限制:

@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    //给filter设置安全管理器
    shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);

    //配置系统受限资源
    //配置系统公共资源
    Map<String,String> map = new HashMap<String,String>();
    map.put("/user/login","anon");//anon 设置为公共资源 
    map.put("/user/register","anon");//anon 设置为公共资源
    map.put("/register.jsp","anon");//anon 设置为公共资源
    map.put("/user/getImage","anon");

    map.put("/**","authc");//authc 请求这个资源需要认证和授权

    //默认认证界面路径---当认证不通过时跳转
    shiroFilterFactoryBean.setLoginUrl("/login.jsp");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

    return shiroFilterFactoryBean;
}

5.5 测试

登录正常,登出正常,未登录和登出后不能访问index.jsp

6. MD5、Salt的认证实现

6.1 springboot整合数据库及mybatis

6.2 数据库表结构及相关对象类添加:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `username` varchar(40) DEFAULT NULL,
  `password` varchar(40) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

在这里插入图片描述
user的entity:

@Data
@TableName("t_user")
public class User {
    @TableId(value = "id", type= IdType.AUTO)
    private String  id;
    @TableField("username")
    private String username;
    @TableField("password")
    private String password;
    @TableField("salt")
    private String salt;
}

service、mapper、xml略

6.3 创建salt工具类

public class SaltUtils {
    /**
     * 生成salt的静态方法
     * @param n
     * @return
     */
    public static String getSalt(int n){
        char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890!@#$%^&*()".toCharArray();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            char aChar = chars[new Random().nextInt(chars.length)];
            sb.append(aChar);
        }
        return sb.toString();
    }
}

6.4 注册相关代码:

6.4.1 添加注册页面:
<body>
<h1>注册界面</h1>
<form action="${pageContext.request.contextPath}/user/register" method="post">
    用户名:<input type="text" name="username" > <br/>
    密码  : <input type="text" name="password"> <br>
    <input type="submit" value="注册">
</form>
</body>
6.4.2 在shiroconfig中添加权限通过
map.put("/user/register","anon");//anon 设置为公共资源 
map.put("/register.jsp","anon");//anon 设置为公共资源  
6.4.3 编写java代码:
@RequestMapping("register")
public String register(User user) {
    try {
        //处理业务调用dao
        //1.生成随机盐
        String salt = SaltUtils.getSalt(8);
        //2.将随机盐保存到数据
        user.setSalt(salt);
        //3.明文密码进行md5 + salt + hash散列
        Md5Hash md5Hash = new Md5Hash(user.getPassword(),salt,1024);
        user.setPassword(md5Hash.toHex());
        userService.save(user);
        return "redirect:/login.jsp";
    }catch (Exception e){
        e.printStackTrace();
        return "redirect:/register.jsp";
    }
}
6.4.4 结果测试:

在这里插入图片描述

6.5 登陆相关代码:

6.5.1 添加自定义realm
//自定义realm
public class CustomRealm2 extends AuthorizingRealm {

	@Autowired
    private IUserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
         //根据身份信息//从传过来的token获取到的用户名
        String principal = (String) token.getPrincipal();

        //根据身份信息查询
        User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, principal));
        System.out.println("User:"+user);

        //用户不为空
        if(!ObjectUtils.isEmpty(user)){
            //返回数据库信息
            // 这里进行数据库中密码和登陆输入密码加盐对比
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),
                    ByteSource.Util.bytes(user.getSalt()), this.getName());
            return simpleAuthenticationInfo;
        }

        return null;
    }
}
6.5.2 修改ShiroConfig中realm

使用凭证匹配器以及hash散列

以及在 getShiroFilterFactoryBean 中添加公共资源

@Bean
public Realm getRealm(){
    CustomRealm2 customerRealm = new CustomRealm2();
    //设置hashed凭证匹配器
    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
    //设置md5加密
    credentialsMatcher.setHashAlgorithmName("md5");
    //设置散列次数
    credentialsMatcher.setHashIterations(1024);
    customerRealm.setCredentialsMatcher(credentialsMatcher);
    return customerRealm;
}

7. 授权实现

7.1 非数据库方式实现:

7.1.1 页面资源授权

在页面的标签上添加权限控制,其中:

  • shiro:hasAnyRoles :是当用户有以下权限时,才能访问此资源
  • shiro:hasPermission :是当用户有此资源访问权限时,才能访问此资源
<%@page contentType="text/html;utf-8" pageEncoding="utf-8" isELIgnored="false" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<%--    受限资源--%>
<h1>系统主页</h1>
<a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
<ul>
    <shiro:hasAnyRoles name="user_manager,admin,addinfo_manager">
        <li><a href="">用户管理</a>
            <ul>
                <shiro:hasPermission name="user:add:*">
                    <li><a href="">添加</a></li>
                </shiro:hasPermission>
                <shiro:hasPermission name="user:delete:*">
                    <li><a href="">删除</a></li>
                </shiro:hasPermission>
                <shiro:hasPermission name="user:update:*">
                    <li><a href="">修改</a></li>
                </shiro:hasPermission>
                <shiro:hasPermission name="user:find:*">
                    <li><a href="">查询</a></li>
                </shiro:hasPermission>
            </ul>
        </li>
    </shiro:hasAnyRoles>
    <shiro:hasAnyRoles name="order_manager,admin,addinfo_manager">
        <li><a href="">订单管理</a></li>
        <ul>
            <shiro:hasPermission name="order:add:*">
                <li><a href="">添加</a></li>
            </shiro:hasPermission>
            <shiro:hasPermission name="order:delete:*">
                <li><a href="">删除</a></li>
            </shiro:hasPermission>
            <shiro:hasPermission name="order:update:*">
                <li><a href="">修改</a></li>
            </shiro:hasPermission>
            <shiro:hasPermission name="order:find:*">
                <li><a href="">查询</a></li>
            </shiro:hasPermission>
        </ul>
    </shiro:hasAnyRoles>
    <shiro:hasRole name="admin">
        <li><a href="">商品管理</a></li>
        <li><a href="">物流管理</a></li>
    </shiro:hasRole>

    <shiro:hasRole name="user">
        <li><a href="">仅普通用户可见</a></li>
        <li><a href="">公共资源</a></li>
    </shiro:hasRole>
</ul>
</body>
</html>

7.1.2 代码方式授权

修改保存接口,只有admin的角色才能进行访问:

@RequestMapping("save")
public String save(){
  System.out.println("进入方法");
  
  //基于角色
  //获取主体对象
  Subject subject = SecurityUtils.getSubject();
  //代码方式
  if (subject.hasRole("admin")) {
    System.out.println("保存订单!");
  }else{
    System.out.println("无权访问!");
  }
  //基于权限字符串
  //....
  return "redirect:/index.jsp";
}
7.1.3 方法调用授权
  • @RequiresRoles 用来基于角色进行授权
  • @RequiresPermissions 用来基于权限进行授权
@Controller
@RequestMapping("order")
public class OrderController {

    @RequiresRoles(value={"admin","user"})//用来判断角色  同时具有 admin user
    @RequiresPermissions("user:update:01") //用来判断权限字符串
    @RequestMapping("save")
    public String save(){
        System.out.println("进入方法");
        return "redirect:/index.jsp";
    }
}
7.1.4 修改CustomRealm2的权限认证方法:
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    //获取身份信息
    String primaryPrincipal = (String) principals.getPrimaryPrincipal();
    System.out.println("调用授权验证: "+primaryPrincipal);
    //根据主身份信息获取角色 和 权限信息
    if ("xiaochen".equals(primaryPrincipal)) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRole("admin");
        authorizationInfo.addRole("admin");

        authorizationInfo.addStringPermission("user:find:*");
        authorizationInfo.addStringPermission("user:update:*");
        authorizationInfo.addStringPermission("order:update:*");

        return authorizationInfo;
    }

    return null;
}
7.1.5 测试

在这里插入图片描述

7.2 数据库方式实现:

在这里插入图片描述

7.2.1 数据库添加
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_perms
-- ----------------------------
DROP TABLE IF EXISTS `t_perms`;
CREATE TABLE `t_pers` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `name` varchar(80) DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_role
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `name` varchar(60) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_role_perms
-- ----------------------------
DROP TABLE IF EXISTS `t_role_perms`;
CREATE TABLE `t_role_perms` (
  `id` int(6) NOT NULL,
  `roleid` int(6) DEFAULT NULL,
  `permsid` int(6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `username` varchar(40) DEFAULT NULL,
  `password` varchar(40) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_user_role
-- ----------------------------
DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (
  `id` int(6) NOT NULL,
  `userid` int(6) DEFAULT NULL,
  `roleid` int(6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

在这里插入图片描述

7.2.2 相关实体类及查询语句添加

7.2.3 复制CustomRealm2为CustomRealm3,修改其中授权方法:
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    //获取身份信息
    String primaryPrincipal = (String) principals.getPrimaryPrincipal();
    System.out.println("调用授权验证: "+primaryPrincipal);
    //根据主身份信息获取角色 和 权限信息
    User user = userService.findRolesByUserName(primaryPrincipal);
    System.out.println("user:"+user);

    //授权角色信息
    if(!CollectionUtils.isEmpty(user.getRoles())){
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        user.getRoles().forEach(role->{
            simpleAuthorizationInfo.addRole(role.getName()); //添加角色信息

            //权限信息
            List<Perm> perms = userService.findPermsByRoleId(role.getId());
            System.out.println("perms:"+perms);

            if(!CollectionUtils.isEmpty(perms) && perms.get(0)!=null ){
                perms.forEach(perm->{
                    simpleAuthorizationInfo.addStringPermission(perm.getName());
                });
            }
        });
        return simpleAuthorizationInfo;
    }
    return null;
}
7.2.4 数据库数据添加

在这里插入图片描述

  • 用户 admin 具有 admin的角色,具有 对于 user,order的所有权限
  • 用户 zhangsan 具有 user的角色,没有权限,只能访问公共资源
  • 用户 usermanager 具有 user_manager的角色,具有 对于 user的所有权限
  • 用户 ordermanager 具有 order_manager的角色,具有 对于 order的所有权限
  • 用户 addinfomanager 具有 addinfo_manager的角色,具有 对于 user,order 的添加权限
7.2.5 测试:

在这里插入图片描述

8. shiro结合缓存

作用:减轻数据库压力

示意图:
在这里插入图片描述

8.1 使用shiro中默认EhCache实现缓存

8.1.1 添加依赖
<!--引入shiro和ehcache-->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-ehcache</artifactId>
  <version>1.5.3</version>
</dependency>

8.1.2 代码修改
@Bean
public Realm getRealm(){
    CustomRealm2 customerRealm = new CustomRealm2();
    //设置hashed凭证匹配器
    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
    //设置md5加密
    credentialsMatcher.setHashAlgorithmName("md5");
    //设置散列次数
    credentialsMatcher.setHashIterations(1024);
    customerRealm.setCredentialsMatcher(credentialsMatcher);

    //开启缓存管理器
    customerRealm.setCacheManager(new EhCacheManager());
    customerRealm.setCachingEnabled(true); // 开启全局缓存
    customerRealm.setAuthorizationCachingEnabled(true); // 授权缓存
    customerRealm.setAuthenticationCachingEnabled(true);// 认证缓存
    
    return customerRealm;
}
8.1.3 测试

登陆后刷新页面发现没有再走数据库查询代码

8.1.3 优缺点
  • 优点:
    • 无需自己编写代码,shiro和ehcache集成,登录后自动缓存登陆信息
  • 缺点:
    • 应用内缓存,一但服务宕机,缓存也随机消失
    • 不适用于分布式项目

8.2 使用redis作为缓存

8.2.1 redis配置

8.2.2 创建redis的缓存管理器

通过查看ehcache的源码,可以看到其实实现了CacheManager接口,所以这里我们自定义redis的缓存管理器实现CacheManager接口

//自定义shiro缓存管理器
public class RedisCacheManager implements CacheManager {

    //参数1:认证或者是授权缓存的统一名称
    @Override
    public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException {
        System.out.println(cacheName);
        return new RedisCache<K,V>(cacheName);
    }
}

从上述返回值来看,如果要取缓存,还得用到shiro的Cache<K, V>的实现,所以我们还得自定义一个类来实现Cache<K, V>接口

8.2.3 自定义redis缓存实现

此实现类中的方法即shiro自动缓存访问的方法。

//自定义redis缓存的实现
public class RedisCache<k,v> implements Cache<k,v> {

    private String cacheName;

    public RedisCache() {
    }

    public RedisCache(String cacheName) {
        this.cacheName = cacheName;
    }

    @Override
    public v get(k k) throws CacheException {
        System.out.println("get key:"+k);
        return (v) getRedisTemplate().opsForHash().get(this.cacheName,k.toString());
    }

    @Override
    public v put(k k, v v) throws CacheException {
        System.out.println("put key: "+k);
        System.out.println("put value:"+v);
        getRedisTemplate().opsForHash().put(this.cacheName,k.toString(),v.toString());
        return null;
    }

    @Override
    public v remove(k k) throws CacheException {
        System.out.println("=============remove=============");
        return (v) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString());
    }

    @Override
    public void clear() throws CacheException {
        System.out.println("=============clear==============");
        getRedisTemplate().delete(this.cacheName);
    }

    @Override
    public int size() {
        return getRedisTemplate().opsForHash().size(this.cacheName).intValue();
    }

    @Override
    public Set<k> keys() {
        return getRedisTemplate().opsForHash().keys(this.cacheName);
    }

    @Override
    public Collection<v> values() {
        return getRedisTemplate().opsForHash().values(this.cacheName);
    }

    private RedisTemplate getRedisTemplate(){
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}
8.2.4 启动登陆测试:

在这里插入图片描述

9. 加入验证码验证

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/191284.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Golang——包

1、GOPATH 项目代码肯定要需要保存在一个目录中&#xff0c;但是如果目录不统一&#xff0c;每个人有一套自己的目录结构&#xff0c;读取配置文件的位置不统一&#xff0c;输出的二进制运行文件也不统一&#xff0c;这样会导致开发的标准不统一。 所以&#xff0c;产生环境变量…

QEMU安装Windows 11的完整过程

零、环境介绍 宿主机&#xff1a; Ubuntu 22.04.1 LTS Windows 11镜像&#xff1a; Win11_Chinese(Simplified)_x64v1 QEMU版本&#xff1a; qemu-img version 7.1.0 Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers 一、安装过程 1. 创建…

随机过程与排队论(二)

随机试验 如果一个试验E满足下列条件&#xff0c;就称此试验为随机试验&#xff1a; 在相同条件下可以重复进行。每次试验的结果不止一个&#xff0c;并且能事先明确知道试验的所有结果。一次试验结束之前&#xff0c;不能确定哪一个结果会出现。 样本空间、随机事件体 随机…

估值85亿美元!智驾前装赛道又添新“巨头”,已开始量产交付

随着智能汽车技术与供应链的发展&#xff0c;可以看到很多高端汽车也逐渐开始采用过去在L4上才使用的传感器&#xff0c;例如激光雷达。同时&#xff0c;多传感器融合技术也已进入规模化量产阶段&#xff0c;为L2在乘用车上的大规模应用打开了一个新窗口。 而作为L4领域的资深…

Leetcode力扣秋招刷题路-0124

从0开始的秋招刷题路&#xff0c;记录下所刷每道题的题解&#xff0c;帮助自己回顾总结 124. 二叉树中的最大路径和&#xff08;Hard&#xff09; 路径 被定义为一条从树中任意节点出发&#xff0c;沿父节点-子节点连接&#xff0c;达到任意节点的序列。同一个节点在一条路径序…

智能驾驶 车牌检测和识别(五)《C++实现车牌检测和识别(可实时车牌识别)》

智能驾驶 车牌检测和识别&#xff08;五&#xff09;《C实现车牌检测和识别&#xff08;可实时车牌识别&#xff09;》 目录 智能驾驶 车牌检测和识别&#xff08;五&#xff09;《C实现车牌检测和识别&#xff08;可实时车牌识别&#xff09;》 1. 前言 2. 车牌检测模型&a…

栈与队列——滑动窗口最大值

力扣题目链接 239. 滑动窗口最大值 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1a; 输入&#xff1a…

CATIA等设计类软件实时渲染流化解决方案

CATIA软件在汽车、航空航天、船舶制造、厂房设计(尤其是钢构厂房)、建筑、通用机械制造等领域&#xff0c;提供3D设计和模拟解决方案。可以帮助企业在产品研发领域缩短开发周期&#xff0c;因此使用非常广泛。但随着技术和设备的发展&#xff0c;CATIA模型不仅仅需要在电脑上进…

活体识别5:论文笔记之FeatherNets

说明 这篇文章是这次比赛的第三名&#xff1a;ChaLearn Face Anti-spoofing Attack Detection ChallengeCVPR2019&#xff0c;此次比赛项目是人脸防欺诈攻击检测。 论文标题&#xff1a;《FeatherNets: Convolutional Neural Networks as Light as Feather for Face Anti-spo…

中科蓝讯读取CSV文件中地址来指定地址段烧录

优势&#xff1a;可不需要通过小牛测控来写码&#xff0c;在烧录的时候直接进行读取文件来写码&#xff0c;可节省小牛测控写码并复位耳机的时间 功能&#xff1a;通过读取外置的 excel 表格里面的配置项&#xff0c;实现对 setting 文件里面的特定配置项的值 进行设置&#…

详解 k8s 中的 RBAC

Kubernetes 主要通过 API Server 对外提供服务&#xff0c;对于这样的系统来说&#xff0c;如果不加以安全限制&#xff0c;那么可能导致请求被滥用&#xff0c;甚至导致整个集群崩塌。 Kubernetes 中提供了良好的多租户认证管理机制&#xff0c;RBAC正式其中重要的一个&#…

Linux驱动开发基础__异步通知

目录 1 适用场景 2 使用流程 3 驱动编程 4 应用编程 5 代码 5.1 gpio_key_drv.c 5.2 button_test.c 5.3 Makefile 6 异步通知机制内核代码详解 1 适用场景 在前面引入中断时&#xff0c;我们曾经举过一个例子&#xff1a; 妈妈怎么知道卧室里小孩醒了&#xff1f; 异…

【深度学习】U-Net和FCN具体分析

FCN 相比于普通分类网络而言:FCN把后面几个全连接都换成卷积,这样就可以获得一张2维的feature map,后接softmax获得每个像素点的分类信息,从而解决了像素级分割问题。 整个FCN网络基本原理如图5**(只是原理示意图)**: image经过多个conv和+一个max pooling变为pool1 f…

SpringCloud_Alibaba Sentinel实现熔断与限流

目录一、Sentinel介绍1.官网2.是什么3.能干嘛4.去哪下5.怎么玩二、安装Sentinel控制台1.sentinel组件由2部分组成2.安装步骤三、初始化演示工程1.启动Nacos8848成功2.案例3.启动Sentinel80804.启动微服务84015.启动8401微服务后查看sentienl控制台四、流控规则1.基本介绍2.流控…

计算机网络 | 网络层知识点期末汇总【还不赶紧收藏】

看不完就慢慢看&#xff0c;扎实掌握&#x1f44a;一、网络层的几个重要概念1、互联网设计思路2、虚电路与数据报服务3、网络层的两个层面二、网际协议 IP1、配套协议2、互连虚拟网络3、IP地址&#xff08;1&#xff09;IP 地址及其表示方法&#xff08;2&#xff09;分类的 IP…

基于 PyTorch 的目标检测和跟踪(无敌版)

一个不知名大学生&#xff0c;江湖人称菜狗 original author: jacky Li Email : 3435673055qq.com Time of completion&#xff1a;2023.2.1 Last edited: 2023.2.1 目录 图像中的目标检测 视频中的目标跟踪 作者有言 在文章《基于 PyTorch 的图像分类器》中&#xff0c;介绍…

网卡ID简要说明

一、概述 网卡ID标识着网卡的具体类型&#xff0c;由五个ID共同确认。根据这五个ID可以在公示网站查到具体的网卡型号。 1. Class id (1) 区分不同的PCI(外设)设备 (2) 网卡类型是&#xff1a;0200 (3) 查询网址&#xff1a;http://pci-ids.ucw.cz/read/PD 2. Vendor id: …

15_open_basedir绕过

open_basedir绕过 一、了解open_basedir 1. 搭建环境 在test目录下存在一个open_basedir.php的文件 里面的php代码就是简单的文件包含或者ssrf,利用的是file_get_contents函数 open_basedir也就是在这种文件包含或者ssrf访问其它文件的时候生效 然后在www目录下再新建一个t…

(隐私计算)联邦学习概述

一、是什么 概念 联邦学习&#xff08;Federated Learning&#xff0c;FELE&#xff09;是一种打破数据孤岛、释放 AI 应用潜能的分布式机器学习技术&#xff0c;能够让联邦学习各参与方在不披露底层数据和底层数据加密&#xff08;混淆&#xff09;形态的前提下&#xff0c;…

Unity-TCP-网络聊天功能(一): API、客户端服务器、数据格式、粘包拆包

1.TCP相关API介绍与服务端编写TCP是面向连接的。因此需要创建监听器&#xff0c;监听客户端的连接。当连接成功后&#xff0c;会返回一个TcpClient对象。通过TcpClient可以接收和发送数据。VS创建C# .net控制台应用项目中创建文件夹Net&#xff0c;Net 下添加TCPServer.cs类&am…