Python 官方文档:入门教程 => 点击学习
目录1.准备工作2.编写index,login,reGISter三个jsP3.实现User、Role、Permission三个POJO4.实现Controller、Service、D
demo源码
此demo用SpringBoot+Shiro简单实现了登陆、注册、认证、授权的功能,并用redis做分布式缓存提高性能。
导入pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="Http://Maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<groupId>com.eGo</groupId>
<artifactId>shirodemo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-WEB</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>Mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.13</version>
</dependency>
<!-- 引入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>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.5.3</version>
</dependency>
<!-- mybatis plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!-- Druid数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- Mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.5.3</version>
</dependency>
<!-- 引入redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
</project>
配置yml文件
spring:
# 设置视图模板为jsp
mvc:
view:
prefix: /
suffix: .jsp
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=UTC
username: root
passWord: root
# 监控统计拦截的filters
filters: stat,wall,log4j,config
# 配置初始化大小/最小/最大
initial-size: 5
min-idle: 5
max-active: 20
# 获取连接等待超时时间
max-wait: 60000
# 间隔多久进行一次检测,检测需要关闭的空闲连接
time-between-eviction-runs-millis: 60000
# 一个连接在池中最小生存的时间
min-evictable-idle-time-millis: 300000
validation-query: SELECT 'x'
test-while-idle: true
test-on-borrow: false
test-on-return: false
# 打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
pool-prepared-statements: false
max-pool-prepared-statement-per-connection-size: 20
redis:
host: 127.0.0.1
port: 6379
password: abc123456
database: 0
mybatis-plus:
type-aliases-package: com.ego.pojo
configuration:
map-underscore-to-camel-case: true
<%--解决页面乱码--%>
<%@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>index</title>
</head>
<body>
<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>
<%--解决页面乱码--%>
<%@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>index</title>
</head>
<body>
<h1>系统主页</h1>
<a href="${pageContext.request.contextPath}/user/logout">退出用户</a>
<ul>
<%-- admin角色的用户能同时拥有用户管理和订单管理的权限,user角色的用户只拥有订单管理的权限 --%>
<shiro:hasRole name="admin">
<li>
<a href="">用户管理</a>
</li>
</shiro:hasRole>
<%-- admin角色的用户对订单有增删改查的权限,user角色的用户只能查看订单 --%>
<shiro:hasAnyRoles name="admin,user">
<li>
<a href="">订单管理</a>
<ul>
<shiro:hasPermission name="order:add:*">
<li><a href="">新增</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="order:del:*">
<li><a href="">删除</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="order:upd:*">
<li><a href="">修改</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="order:find:*">
<li><a href="">查询</a></li>
</shiro:hasPermission>
</ul>
</li>
</shiro:hasAnyRoles>
</ul>
</body>
</html>
<%--解决页面乱码--%>
<%@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>register</title>
</head>
<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>
</html>
package com.ego.pojo;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.apiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_user")
@ApiModel("用户实体类")
public class User implements Serializable {
@TableId(type = IdType.AUTO)
@ApiModelProperty(name = "id", value = "ID 主键")
private Integer id;
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(name = "username", value = "用户名")
private String username;
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(name = "password", value = "密码")
private String password;
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty(name = "salt", value = "盐")
private String salt;
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(name = "age", value = "年龄")
private Integer age;
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(name = "email", value = "邮箱")
private String email;
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(name = "address", value = "地址")
private String address;
@TableField(exist = false)
private List<Role> roles = new ArrayList<>();
}
package com.ego.pojo;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_role")
@ApiModel("角色实体类")
public class Role implements Serializable {
@TableId(type = IdType.AUTO)
@ApiModelProperty(name = "id", value = "ID 主键")
private Integer id;
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(name = "name", value = "角色名称")
private String name;
@TableField(exist = false)
private List<Permission> permissions = new ArrayList<>();
}
package com.ego.pojo;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_permission")
@ApiModel("权限实体类")
public class Permission implements Serializable {
@TableId(type = IdType.AUTO)
@ApiModelProperty(name = "id", value = "ID主键")
private Integer id;
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(name = "name", value = "权限名称")
private String name;
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(name = "url", value = "权限菜单URL")
private String url;
}
这里dao采用了mybatis-plus
package com.ego.controller;
import com.ego.pojo.User;
import com.ego.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/login")
public String login(String username,String password){
// 获取当前登录用户
Subject subject = SecurityUtils.getSubject();
try {
// 执行登录操作
subject.login(new UsernamePasswordToken(username,password));
// 认证通过后直接跳转到index.jsp
return "redirect:/index.jsp";
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误!");
} catch (IncorrectCredentialsException e) {
System.out.println("密码错误!");
} catch (Exception e) {
}
// 如果认证失败仍然回到登录页面
return "redirect:/login.jsp";
}
@RequestMapping("/logout")
public String logout(){
subject.logout();
// 退出后仍然会到登录页面
* 用户注册
* @param user
@RequestMapping("/register")
public String register(User user){
userService.register(user);
return "redirect:/login.jsp";
return "redirect:/register.jsp";
}
package com.ego.service.impl;
import com.ego.dao.mapper.UserMapper;
import com.ego.shiro.ShiroConstant;
import com.ego.utils.SaltUtil;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
private UserMapper userMapper;
@Override
public void register(User user) {
//生成随机盐
String salt = SaltUtil.getSalt(ShiroConstant.SALT_LENGTH);
//保存随机盐
user.setSalt(salt);
//生成密码
Md5Hash password = new Md5Hash(user.getPassword(), salt, ShiroConstant.HASH_ITERATORS);
//保存密码
user.setPassword(password.toHex());
userMapper.insert(user);
public User findUserByUserName(String userName) {
return userMapper.findUserByUserName(userName);
import com.ego.dao.mapper.RoleMapper;
import com.ego.pojo.Role;
import com.ego.service.RoleService;
import java.util.List;
@Service("roleService")
public class RoleServiceImpl implements RoleService {
private RoleMapper roleMapper;
public List<Role> getRolesByUserId(Integer userId) {
return roleMapper.getRolesByUserId(userId);
import com.ego.dao.mapper.PermissionMapper;
import com.ego.pojo.Permission;
import com.ego.service.PermissionService;
@Service("permissionService")
public class PermissionServiceImpl implements PermissionService {
private PermissionMapper permissionMapper;
public List<Permission> getPermissionsByRoleId(Integer roleId) {
return permissionMapper.getPermissionsByRoleId(roleId);
package com.ego.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT u.id,u.username,u.password,u.salt,u.age,u.email,u.address FROM t_user u WHERE u.username = #{username}")
User findUserByUserName(String username);
public interface RoleMapper extends BaseMapper<Role> {
@Select("select r.id,r.name from t_role r left join t_user_role ur on ur.role_id = r.id where ur.user_id = #{userId}")
List<Role> getRolesByUserId(Integer userId);
public interface PermissionMapper extends BaseMapper<Permission> {
@Select("select p.id,p.name,p.url from t_permission p left join t_role_permission rp on rp.permission_id = p.id where rp.role_id = #{roleId}")
List<Permission> getPermissionsByRoleId(Integer roleId);
package com.ego.utils;
import java.util.Random;
public class SaltUtil {
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();
}
}
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextUtil implements ApplicationContextAware {
public static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
public static Object getBean(String beanName){
return context.getBean(beanName);
package com.ego.utils;
import java.util.Random;
public class SaltUtil {
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();
}
}
package com.ego.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextUtil implements ApplicationContextAware {
public static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
public static Object getBean(String beanName){
return context.getBean(beanName);
}
}
package com.ego.shiro;
import com.ego.shiro.cache.RedisCacheManager;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfiguration {
//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("/user/login", "anon");
map.put("/user/register","anon");
map.put("/register.jsp","anon");
map.put("/index.jsp","authc");//authc 请求这个资源需要认证和授权
//默认认证界面路径
shiroFilterFactoryBean.setLoginUrl("/login.jsp");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
//2.创建安全管理器
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//给安全管理器设置
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
//3.创建自定义realm
public Realm getRealm(){
CustomerRealm customerRealm = new CustomerRealm();
// 设置密码匹配器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
// 设置加密方式
credentialsMatcher.setHashAlgorithmName(ShiroConstant.HASH_ALGORITHM_NAME.MD5);
// 设置散列次数
credentialsMatcher.setHashIterations(ShiroConstant.HASH_ITERATORS);
customerRealm.setCredentialsMatcher(credentialsMatcher);
// 设置缓存管理器
customerRealm.setCacheManager(new RedisCacheManager());
// 开启全局缓存
customerRealm.setCachingEnabled(true);
// 开启认证缓存并指定缓存名称
customerRealm.setAuthenticationCachingEnabled(true);
customerRealm.setAuthenticationCacheName("authenticationCache");
// 开启授权缓存并指定缓存名称
customerRealm.setAuthorizationCachingEnabled(true);
customerRealm.setAuthorizationCacheName("authorizationCache");
return customerRealm;
}
public class ShiroConstant {
public static final int SALT_LENGTH = 8;
public static final int HASH_ITERATORS = 1024;
public interface HASH_ALGORITHM_NAME {
String MD5 = "MD5";
import com.ego.pojo.Permission;
import com.ego.pojo.Role;
import com.ego.pojo.User;
import com.ego.service.PermissionService;
import com.ego.service.RoleService;
import com.ego.service.UserService;
import com.ego.utils.ApplicationContextUtil;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.util.List;
public class CustomerRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 获取主身份信息
String principal = (String) principals.getPrimaryPrincipal();
// 根据主身份信息获取角色信息
UserService userService = (UserService) ApplicationContextUtil.getBean("userService");
User user = userService.findUserByUserName(principal);
RoleService roleService = (RoleService) ApplicationContextUtil.getBean("roleService");
List<Role> roles = roleService.getRolesByUserId(user.getId());
if(!CollectionUtils.isEmpty(roles)){
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
roles.forEach(role -> {
simpleAuthorizationInfo.addRole(role.getName());
PermissionService permissionService = (PermissionService) ApplicationContextUtil.getBean("permissionService");
List<Permission> permissions = permissionService.getPermissionsByRoleId(role.getId());
if(!CollectionUtils.isEmpty(permissions)){
permissions.forEach(permission -> {
simpleAuthorizationInfo.addStringPermission(permission.getName());
});
}
});
return simpleAuthorizationInfo;
}
return null;
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String principal = (String) token.getPrincipal();
if(!ObjectUtils.isEmpty(user)){
return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), new CustomerByteSource(user.getSalt()),this.getName());
import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.codec.Hex;
import org.apache.shiro.util.ByteSource;
import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Arrays;
//自定义salt实现 实现序列化接口
public class CustomerByteSource implements ByteSource, Serializable {
private byte[] bytes;
private String cachedHex;
private String cachedBase64;
public CustomerByteSource() {
public CustomerByteSource(byte[] bytes) {
this.bytes = bytes;
public CustomerByteSource(char[] chars) {
this.bytes = CodecSupport.toBytes(chars);
public CustomerByteSource(String string) {
this.bytes = CodecSupport.toBytes(string);
public CustomerByteSource(ByteSource source) {
this.bytes = source.getBytes();
public CustomerByteSource(File file) {
this.bytes = (new CustomerByteSource.BytesHelper()).getBytes(file);
public CustomerByteSource(InputStream stream) {
this.bytes = (new CustomerByteSource.BytesHelper()).getBytes(stream);
public static boolean isCompatible(Object o) {
return o instanceof byte[] || o instanceof char[] || o instanceof String || o instanceof ByteSource || o instanceof File || o instanceof InputStream;
public byte[] getBytes() {
return this.bytes;
public boolean isEmpty() {
return this.bytes == null || this.bytes.length == 0;
public String toHex() {
if (this.cachedHex == null) {
this.cachedHex = Hex.encodeToString(this.getBytes());
return this.cachedHex;
public String toBase64() {
if (this.cachedBase64 == null) {
this.cachedBase64 = Base64.encodeToString(this.getBytes());
return this.cachedBase64;
public String toString() {
return this.toBase64();
public int hashCode() {
return this.bytes != null && this.bytes.length != 0 ? Arrays.hashCode(this.bytes) : 0;
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof ByteSource) {
ByteSource bs = (ByteSource) o;
return Arrays.equals(this.getBytes(), bs.getBytes());
} else {
return false;
private static final class BytesHelper extends CodecSupport {
private BytesHelper() {
public byte[] getBytes(File file) {
return this.toBytes(file);
public byte[] getBytes(InputStream stream) {
return this.toBytes(stream);
到此这篇关于SpringBoot整合Shiro和Redis的文章就介绍到这了,更多相关SpringBoot整合Shiro和Redis内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
--结束END--
本文标题: SpringBoot整合Shiro和Redis的示例代码
本文链接: https://lsjlt.com/news/145371.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-03-01
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0