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

目录

前言

一、Spring Boot、Redis和Shiro是什么?

二、使用步骤

1.引入相关的依赖

2、准备好相应的数据库

3、搭建出项目框架

(1)config层

1、shiroconfig

 (2)controller控制层

1、LoginController类

2、UserController类

(3)dao层

1、UserDao

 2、PermissionDao

(4) entity实体类

1、User

2、Permission 

(5)handler捕获控制层的异常

(6)Myrealm类

 (7)service业务层

1、UserService类

2、PermissionService类

 (8)vo类

1、LoginVo映射前端登录的密码和账号

2、Result类

 (9)mapper

(10)前端页面

1、login登录页面

 2、sucess成功页面

(11) application.properties配置

2.测试

总结

前言

将Spring Boot、Redis和Shiro整合在一起具有多个优势。首先,通过与Spring Boot的集成,可以简化开发过程,利用其自动配置和约定优于配置的原则。其次,Redis作为高性能的缓存和存储系统,可以提供快速的数据访问和响应时间,通过与Spring Boot和Shiro的整合,可以实现更高效的会话管理和身份验证。此外,通过将Shiro的会话存储在Redis中,可以实现分布式会话管理和高可用性,并支持共享会话和无状态应用程序架构。综上所述,整合Spring Boot、Redis和Shiro能够提供性能、灵活性和安全性的综合优势,使开发者能够更专注于业务逻辑而不必过多关注底层的配置和细节。

一、Spring Boot、Redis和Shiro是什么?

Spring Boot: Spring Boot是一个基于Spring框架的开发框架,旨在简化和加快Spring应用程序的开发过程。它提供了自动配置和约定优于配置的原则,使得开发者可以更专注于业务逻辑而不必花费过多精力在配置上。Spring Boot还提供了内嵌的Servlet容器,使得应用程序可以独立运行,无需外部的应用服务器。

Redis: Redis是一种高性能的键值存储系统,它提供了快速的数据访问和持久化。Redis支持多种数据结构,如字符串、哈希表、列表、集合和有序集合,同时还提供了丰富的命令集合来操作这些数据结构。Redis被广泛用于缓存、消息队列、会话管理和实时数据处理等场景。

Shiro: Shiro是一个强大且易于使用的Java安全框架,用于身份验证、授权和会话管理。它提供了一套灵活且可扩展的API,使得开发者可以轻松地集成安全功能到应用程序中。Shiro支持各种认证方式,如用户名密码、LDAP、OAuth等,并提供了细粒度的授权控制,以及灵活的会话管理和加密功能。

二、使用步骤

下面通过一个用户登录权限的案例来使用一下springboot整合shiro和redis,首先先搭建一个springboot工程,这里使用的是2.3.12.RELEASE版本的

1.引入相关的依赖

    
        org.springframework.boot
        spring-boot-starter-parent
        2.3.12.RELEASE
         
    
    com.hlg
    spring-shiro01
    0.0.1-SNAPSHOT
    spring-shiro01
    spring-shiro01
    
        1.8
    
           
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
        
            mysql
            mysql-connector-java
        
        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.5.1
        
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
        
            org.projectlombok
            lombok
        
        
        
            org.apache.shiro
            shiro-spring-boot-starter
            1.7.0
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
        
            io.github.jianzhichun
            spring-boot-starter-swagger2
            0.0.1
        
        
            com.github.xiaoymin
            swagger-bootstrap-ui
            1.9.6
        
        
        
            com.github.theborakompanioni
            thymeleaf-extras-shiro
            2.0.0
        
        
        
            org.crazycake
            shiro-redis
            3.3.1
        
    

2、准备好相应的数据库

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------

-- Table structure for permission

-- ----------------------------

DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission`  (
  `perid` int(0) NOT NULL AUTO_INCREMENT,
  `pername` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `percode` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`perid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------

-- Records of permission

-- ----------------------------

INSERT INTO `permission` VALUES (1, '查询用户信息', 'user:query');
INSERT INTO `permission` VALUES (2, '修改用户', 'user:update');
INSERT INTO `permission` VALUES (3, '删除用户', 'user:delete');
INSERT INTO `permission` VALUES (4, '添加用户', 'user:insert');
INSERT INTO `permission` VALUES (5, '导出用户', 'user:export');

-- ----------------------------

-- Table structure for role

-- ----------------------------

DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `roleid` int(0) NOT NULL AUTO_INCREMENT,
  `rolename` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`roleid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_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(0) NOT NULL,
  `roleid` int(0) NOT NULL,
  PRIMARY KEY (`perid`, `roleid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------

-- Records of role_permission

-- ----------------------------

INSERT INTO `role_permission` VALUES (1, 1);
INSERT INTO `role_permission` VALUES (1, 2);
INSERT INTO `role_permission` VALUES (1, 3);
INSERT INTO `role_permission` VALUES (2, 1);
INSERT INTO `role_permission` VALUES (2, 2);
INSERT INTO `role_permission` VALUES (3, 1);
INSERT INTO `role_permission` VALUES (3, 2);
INSERT INTO `role_permission` VALUES (4, 1);
INSERT INTO `role_permission` VALUES (5, 3);

-- ----------------------------

-- Table structure for user

-- ----------------------------

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `userid` int(0) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `userpwd` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `sex` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `address` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  `salt` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`userid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------

-- Records of user

-- ----------------------------

INSERT INTO `user` VALUES (1, 'ykk', '4b05a8716a430f71c6911d2e9149092f', '男', '武汉', '07f63bbf285e4719bfee1cc2bb81db33');
INSERT INTO `user` VALUES (2, 'wzy', '99cc11855b41ff3e5f7c0af3c5090a8c', '女', '北京', '2d7e37f019144764ac02aa7220929f93');
INSERT INTO `user` VALUES (3, 'wangwu', '6d70c027801af4dc892ab340d4bf73b6', '女', '成都', '99f6bf1b540145699e6e5c90480105b0');

-- ----------------------------

-- Table structure for user_role

-- ----------------------------

DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `userid` int(0) NOT NULL,
  `roleid` int(0) NOT NULL,
  PRIMARY KEY (`userid`, `roleid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_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;

3、搭建出项目框架

 
  

如果不想自己搭建,这里也为大家准备了

(1)config层

1、shiroconfig
@Configuration
public class ShiroConfig {
        //Swagger文档配置
    @Bean
    public Docket docket(){
        Docket docket=new Docket(DocumentationType.SWAGGER_2)
                .groupName("cc666.Ykk-job")
                .apiInfo(getInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.hlg.controller"))
                .build();
        return docket;
    }
    private ApiInfo getInfo() {
        Contact DEFAULT_CONTACT=new Contact("火龙果","http://www.jd.com","[email protected]");
        ApiInfo apiInfo=new ApiInfo("springboot-shiro信息管理系统API文档","springboot-shiro管理系统API文档","6.66","localhost:8080/doc.html"
                ,DEFAULT_CONTACT,"Kunkun科技有限公司","http://www.baidu.com");
        return apiInfo;
    }
    @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm());
        securityManager.setCacheManager(redisCacheManager());
        return securityManager;
    }
    @Bean
    public RedisCacheManager redisCacheManager(){
        RedisCacheManager redisCacheManager=new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager=new RedisManager();
        redisManager.setHost("redis的ip地址:6379");//这里需要改为自己redis的地址和端口号,默认是6379
        redisManager.setDatabase(0);//0是redis数据库序号
        return redisManager;
    }
    @Bean
    public MyRealm myRealm() {
        //设置密码加密器
        MyRealm myRealm=new MyRealm();
        myRealm.setCredentialsMatcher(credentialsMatcher());
        return myRealm;
    }
//这里的两个变量是设置的密码加密器的类型和加密次数(我设置的是MD5和1024次加密,也可以自己根据需要修改,需要注意的是数据库也需要同步修改)
    @Value("${shiro.hashAlgorithmName}")
    private String hashAlgorithmName;
    @Value("${shiro.hashIterations}")
    private int hashIterations;
    @Bean
    public HashedCredentialsMatcher credentialsMatcher(){
        HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName(hashAlgorithmName);
        credentialsMatcher.setHashIterations(hashIterations);
        return credentialsMatcher;
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean filterFactoryBean(){
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        shiroFilterFactoryBean.setLoginUrl("/login.html");
        Map map=new HashMap<>();
         //静态资源放行
        map. Put("/login","anon");
        map.put("/doc.html","anon");
        map.put("/webjars/**","anon");
        map.put("/swagger-resources/**","anon");
        map.put("v2/**","anon");
        map.put("/**","authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }
    @Bean
    public FilterRegistrationBean filterFilterRegistrationBean(){
        FilterRegistrationBean filterFilterRegistrationBean=new FilterRegistrationBean<>();
        filterFilterRegistrationBean.setFilter(new DelegatingFilterProxy());
        filterFilterRegistrationBean.setName("shiroFilter");
        filterFilterRegistrationBean.addUrlPatterns("/*");
        return filterFilterRegistrationBean;

    }
    /**
     * 这里是为了能在html页面引用shiro标签,
     */
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

}

 (2)controller控制层

1、LoginController类
@Controller
@Api(tags = "登录接口类")
public class LoginController {
        @PostMapping("/login")
        @ApiOperation(value = "登录接口")
        @ApiImplicitParam(name = "账号和密码对象",value = "loginVo")
        public String login(LoginVo loginVo){
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(loginVo.getUsername(), loginVo.getPassword());
            try {
                subject.login(token);
                return "success";
            } catch (Exception e) {
                e.printStackTrace();//
                return "redirect:/login.html";
            }
        }
}
2、UserController类
@RestController
@RequestMapping("/user")
@Api(tags = "权限测试接口类")
public class UserController {
    @GetMapping("/query")
    @RequiresPermissions(value = "user:query")
    @ApiOperation(value = "查询权限接口")
    public String query(){
        return "rnm1LKwdjaIKLdw";
    }

    @GetMapping("/update")
    @RequiresPermissions(value = "user:update")
    @ApiOperation(value = "修改权限接口")
    public String update(){
        return "user:update------------------------";
    }

    @GetMapping("/delete")
    @RequiresPermissions(value = "user:delete")
    @ApiOperation(value = "删除权限接口")
    public String delete(){
        return "user:delete------------------------";
    }

    @GetMapping("/insert")
    @RequiresPermissions(value = "user:insert")
    @ApiOperation(value = "添加权限接口")
    public String insert(){
        return "user:insert------------------------";
    }

    @GetMapping("/export")
    @RequiresPermissions(value = "user:export")
    @ApiOperation(value = "导出权限接口")
    public String export(){
        return "user:export------------------------";
    }
}

(3)dao层

1、UserDao
public interface UserDao extends BaseMapper {
}
 2、PermissionDao
public interface PermissionDao extends BaseMapper {
    List findRoletById(Integer id);
}

(4) entity实体类

1、User
@Data
public class User {
    @TableId(type = IdType.AUTO,value = "userid")
    private Integer id;
    private String username;
    private String userpwd;
    private String sex;
    private String address;
    private String salt;
}
2、Permission 
@Data
public class Permission {
    @TableId(type = IdType.AUTO)
    private Integer perid;
    private String pername;
    private String percode;
}
(5)handler捕获控制层的异常
@ControllerAdvice
public class MyHandler {
     //这里只是捕获了权限不足的异常,也可以捕获登录时密码错误或者其他异常
    @ExceptionHandler(value = AuthorizationException.class)
    @ResponseBody
    public Result myhadnler(AuthorizationException e){
        e.printStackTrace();
        return new Result(403,"权限不足",null);
    }
}

(6)Myrealm类

public class MyRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    @Autowired
    private PermissionService permissionService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        User user = (User) principalCollection.getPrimaryPrincipal();
        List permissions = permissionService.findRoletByUserName(user.getId());
        //用stream流来取出Permission集合中对象的Percode属性并封装到一个新的集合中
        List collect = permissions.stream().map(Permission::getPercode).collect(Collectors.toList());
        if (permissions.size() != 0) {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.addStringPermissions(collect);
            return info;
        }
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String username = authenticationToken.getPrincipal().toString();
        User user = userService.finAllByUserName(username);
        if (user != null) {
            ByteSource salt = ByteSource.Util.bytes(user.getSalt());
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getUserpwd(), salt, this.getName());
            return info;
        }
        return null;
    }
}

 (7)service业务层

1、UserService类
@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    public User finAllByUserName(String username) {
        QueryWrapper queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("username",username);
        //查询user对象信息
        User user = userDao.selectOne(queryWrapper);
        return user;
    }
}
2、PermissionService类
@Service
public class PermissionService {

    @Autowired
    private PermissionDao permissionDao;
    public List findRoletByUserName(Integer userid) {
        List roletById = permissionDao.findRoletById(userid);
        return roletById;
    }
}

 (8)vo类

1、LoginVo映射前端登录的密码和账号
@Data
@ToString
public class LoginVo {
    private String username;
    private String password;

}
2、Result类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
    private Integer code;
    private String msg;
    private Object data;
}

 (9)mapper




    

(10)前端页面

1、login登录页面



  Login
  




 2、sucess成功页面



    
    Title


查询

添加

修改

删除

导出

(11) application.properties配置

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql:///shiro?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root

#??
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

mybatis-plus.mapper-locations=classpath:/mapper/*.xml

#thymeleaf??????
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html


#???????
shiro.hashAlgorithmName=MD5
shiro.hashIterations=1024

2.测试

首先保证redis数据库,mysql数据库处于运行状态,这里使用用户ykk账号登录,用户ykk有增删改查的权限,所有就只能在登录成功后看到增删改查,因为没有导出的属性,如果强行访问导出的权限,就会抛出异常,Handler类就会捕获异常,并先前端返回一个Result类型的JSON对象

1、登录用户ykk的账号

springboot整合shiro和redis(超详细案例演示)_第1张图片

2、主页展示的权限

springboot整合shiro和redis(超详细案例演示)_第2张图片

 3、强行访问导出属性就会返回异常

springboot整合shiro和redis(超详细案例演示)_第3张图片


总结

整合Spring Boot、Redis和Shiro能够提供性能、灵活性和安全性的综合优势,使开发者能够更轻松地构建可靠、高效和安全的应用程序。通过本文提供的指导和示例代码,任何人都可以快速上手并应用这些知识到实际项目中。

你可能感兴趣的:(spring,boot,redis,后端,mybatis,mysql,idea,java)