Springboot+Shiro+Mybatis+mysql實現權限安全認證
Shiro是Apache 的一個強大且易用的Java安全框架,執行身份驗證、授權、密碼學和會話管理。Shiro 主要分為兩個部分就是認證和授權兩部分
一、介紹
-
Subject代表了當前用戶的安全操作
-
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通過SecurityManager來管理內部組件實例,并通過它來提供安全管理的各種服務。
-
Authenticator即認證器,對用戶身份進行認證,Authenticator是一個接口,shiro提供ModularRealmAuthenticator實現類,通過ModularRealmAuthenticator基本上可以滿足大多數需求,也可以自定義認證器。
-
Authorizer即授權器,用戶通過認證器認證通過,在訪問功能時需要通過授權器判斷用戶是否有此功能的操作權限。
-
Realm充當了Shiro與應用安全數據間的“橋梁”或者“連接器”。也就是說,當對用戶執行認證(登錄)和授權(訪問控制)驗證時,Shiro會從應用配置的Realm中查找用戶及其權限信息。
-
sessionManager即會話管理,shiro框架定義了一套會話管理,它不依賴web容器的session,所以shiro可以使用在非web應用上。
Shiro相關類介紹
-
(1)Authentication 認證 —— 用戶登錄
-
(2)Authorization 授權 —- 用戶具有哪些權限
-
(3)Cryptography 安全數據加密
-
(4)Session Management 會話管理
-
(5)Web Integration web系統集成
-
(6)Interations 集成其它應用,spring、緩存框架
二、依賴引入
完整的pom文件如下:
<?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.4.1</version>
<relativePath></relativePath> <!-- lookup parent from repository -->
</parent>
<groupId>com.gt.shiro</groupId>
<artifactId>com.sunyue.shiro</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
<druid.verzion>1.1.10</druid.verzion>
<pagehelper.version>1.2.10</pagehelper.version>
<mybatis.version>2.1.4</mybatis.version>
<thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除默認的tomcat -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 重新依賴Jetty的starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!--shiro整合spring-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.verzion}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper-spring-boot-starter -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</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>
</dependencies>
<build>
<plugins>
<!-- spring boot maven插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.gt.shiro.SpringShiroApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
三、配置文件
application.yml配置文件:
# 開發時關閉緩存,不然沒法看到實時頁面
spring.thymeleaf.cache=false
# 用非嚴格的 HTML
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=utf-8
spring.thymeleaf.servlet.content-type=text/html
spring.datasource.druid.url=jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
spring.datasource.druid.username=root
spring.datasource.druid.password=admin
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
spring.datasource.druid.test-on-borrow=true
#springbootjdbc導入包不和以前一樣
spring.datasource.druid.driver-class-name= com.mysql.cj.jdbc.Driver
mybatis.type-aliases-package=com.gt.shiro.entity
mybatis.mapper-locations=classpath:mapper/*.xml
#打印數據庫的操作
logging.level.com.example.springsecurity.dao=debug
#redis緩存
### 配置Redis
mybatis.configuration.cache-enabled=true
# Redis數據庫索引(默認為0)
spring.redis.database=0
# Redis服務器地址
spring.redis.host=...
# Redis服務器連接端口
spring.redis.port=6379
# Redis服務器連接密碼(默認為空)
spring.redis.password=sunyue
# 連接池最大連接數(使用負值表示沒有限制)
spring.redis.jedis.pool.max-idle=200
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.jedis.pool.max-wait=-1
# 連接池中的最小空閑連接
spring.redis.jedis.pool.min-idle=0
# 連接超時時間(毫秒)
spring.redis.timeout=1000
Shiro兩個重要的配置類:
-
1.UserRealm
package com.gt.shiro.config; import com.gt.shiro.entity.TestUser; import com.gt.shiro.server.TestUserServer; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; 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.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class UserRealm extends AuthorizingRealm { @Autowired private TestUserServer testUserServer; /** * 執行授權邏輯 * * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("執行授權邏輯"); /*獲取當前登錄的用戶信息*/ Subject subject = SecurityUtils.getSubject(); TestUser testUser = (TestUser) subject.getPrincipal(); //設置角色,多個角色 /*Set<String> rolesSet = new HashSet<>(); rolesSet.add(testUser.getRole());*/ //SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(rolesSet); //給資源進行授權 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); /*可以在以下list加入多個權限*/ /*List<String> roles = new ArrayList<>(); roles.add(testUser.getPerms()); info.addRoles(roles);*/ //設置權限 info.addRole(testUser.getRole()); //需要判斷權限是否為空值(null是沒有地址,""是有地址但是里面的內容是空的) if (testUser.getPerms() != null && !testUser.getPerms().equals("")) { info.addStringPermission(testUser.getPerms()); } return info; } /** * 執行認證邏輯 * * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("執行認證邏輯"); /*獲取令牌*/ UsernamePasswordToken passwordToken = (UsernamePasswordToken) authenticationToken; //取出用戶名并且判斷用戶名是否和數據庫一致 TestUser testUser = testUserServer.selectOneByName(passwordToken.getUsername()); if (testUser != null) { //進行認證,將正確數據給shiro處理 //密碼不用自己比對,AuthenticationInfo認證信息對象,一個接口,new他的實現類對象SimpleAuthenticationInfo /* 第一個參數隨便放,可以放user對象,程序可在任意位置獲取 放入的對象 * 第二個參數必須放密碼, * 第三個參數放 當前realm的名字,因為可能有多個realm*/ //若密碼不正確則返回IncorrectCredentialsException異常 return new SimpleAuthenticationInfo(testUser, testUser.getPassword(), this.getName()); } //若用戶名不存在則返回UnknownAccountException異常 return null; } } -
2.ShiroConfig
package com.gt.shiro.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //設置安全管理器 shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager); //添加一些Shiro的內置過濾器 /** * Shiro 的內置過濾器可以實現權限的相關攔截 * 常用過濾器 * 1.anon:無需認證 * 2.authc:必須認證才能訪問 * 3.user:如果使用rememberme功能可以訪問 * 4.perms:對應權限才能訪問 * 5.role:對應角色才能訪問 */ //登錄狀態下才可以訪問main頁面,manage權限可訪問manage頁面,admin角色可訪問admin頁面 Map<String, String> filterMap = new LinkedHashMap<String, String>(); filterMap.put("/main", "authc"); filterMap.put("/manage", "perms[manage]"); filterMap.put("/admin", "roles[admin]"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); //未登錄狀態下訪問將跳轉至login頁面 // 如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登錄成功后要跳轉的鏈接 shiroFilterFactoryBean.setSuccessUrl("/"); //無授限狀態下訪問將請求unauthor shiroFilterFactoryBean.setUnauthorizedUrl("/unAuth"); return shiroFilterFactoryBean; } @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); //DefaultWebSecurityManager需要關聯一個Realm defaultWebSecurityManager.setRealm(userRealm); return defaultWebSecurityManager; } /** * 創建realm */ @Bean(name = "userRealm") public UserRealm getRealm() { return new UserRealm(); } @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } /** * 開啟Shiro的注解(如@RequiresRoles,@RequiresPermissions) * 配置以下兩個bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可實現此功能 * * @return */ @Bean public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } /** * 開啟 shiro 的@RequiresPermissions注解 * * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * shiro出現權限異常可通過此異常實現制定頁面的跳轉(或接口跳轉) * * @return */ @Bean public SimpleMappingExceptionResolver simpleMappingExceptionResolver() { SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties properties = new Properties(); /*未授權處理頁*/ properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/error.html"); /*身份沒有驗證*/ properties.setProperty("org.apache.shiro.authz.UnauthenticatedException", "/error.html"); resolver.setExceptionMappings(properties); return resolver; } }
四、數據連接和業務邏輯
-
1.實體類
package com.gt.shiro.entity; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; import java.util.Date; @Data @Accessors(chain = true) public class TestUser implements Serializable { private Integer id; private String username; private String password; /*權限*/ private String perms; /*角色*/ private String role; /*加鹽密碼*/ private String salt; } -
2.Dao和Mapper
package com.gt.shiro.dao; import com.gt.shiro.entity.TestUser; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface TestUserMapper { List<TestUser> findAll(); TestUser selectOne(Integer id); TestUser selectOneByName(String username); void insert(TestUser testUser); void update(TestUser testUser); void delete(Integer id); }
<?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.gt.shiro.dao.TestUserMapper">
<select id="findAll" resultType="TestUser">
select * from test_user
</select>
<select id="selectOne" resultType="TestUser">
select * from test_user where id=#{id}
</select>
<select id="selectOneByName" resultType="TestUser">
select * from test_user where username=#{username}
</select>
<insert id="insert">
insert into test_user (id,username,password,perms,role,salt) value (#{id},#{username},#{password},#{perms},#{role},#{salt})
</insert>
<update id="update">
update test_user set username = #{username},password=#{password},perms=#{perms},role=#{role},salt=#{salt} where id = #{id}
</update>
<delete id="delete">
delete from test_user where id = #{id}
</delete>
</mapper>
-
3.業務層及其實現
package com.gt.shiro.server; import com.gt.shiro.entity.TestUser; import org.springframework.stereotype.Service; import java.util.List; @Service public interface TestUserServer { /*查詢所有*/ List<TestUser> selectAll(); /*查詢一個用戶*/ TestUser selectByOne(Integer id); /*通過名字查詢一個用戶*/ TestUser selectOneByName(String name); /*增加一個用戶*/ void insert(TestUser testUser); /*刪除一個用戶*/ void delete(Integer id); /*更新一個用戶*/ void update(TestUser testUser); }
package com.gt.shiro.server.serverImpl;
import com.gt.shiro.dao.TestUserMapper;
import com.gt.shiro.entity.TestUser;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.sunyue.shiro.server.TestUserServer;
import java.util.List;
@Service
public class TestUserServerImpl implements TestUserServer {
@Autowired
private TestUserMapper testUserMapper;
@Override
public List<TestUser> selectAll() {
return testUserMapper.findAll();
}
@Override
public TestUser selectByOne(Integer id) {
return testUserMapper.selectOne(id);
}
@Override
public TestUser selectOneByName(String name) {
return testUserMapper.selectOneByName(name);
}
@Override
public void insert(TestUser testUser) {
//加密寫法
String salt = new SecureRandomNumberGenerator().nextBytes().toString();
String password= new SimpleHash("md5",testUser.getPassword(),salt,2).toString();
testUser.setPassword(password);
testUser.setSalt(salt);
testUserMapper.insert(testUser);
}
@Override
public void delete(Integer id) {
testUserMapper.delete(id);
}
@Override
public void update(TestUser testUser) {
testUserMapper.update(testUser);
}
}
-
4.控制層
package com.gt.shiro.controller; import com.gt.shiro.entity.TestUser; import com.gt.shiro.server.TestUserServer; 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.crypto.hash.SimpleHash; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @Controller public class indexController { @Autowired private TestUserServer testUserServer; @GetMapping("/{url}") public String redirect(@PathVariable("url") String url) { return url; } @RequestMapping(value = {"/", "/index"}, method = RequestMethod.GET) private String index() { return "index"; } @PostMapping("/login") public String login(String username, String password, Model model) { Subject subject = SecurityUtils.getSubject(); TestUser testUser = testUserServer.selectOneByName(username); if (testUser != null) { //根據salt值和用戶輸入的密碼計算加密后的密碼 String salt = testUser.getSalt(); password = new SimpleHash("md5", password, salt, 2).toString(); System.out.println(password); } UsernamePasswordToken token = new UsernamePasswordToken(username, password); //UsernamePasswordToken token = new UsernamePasswordToken(username, testUser.getPassword());(不加密寫法) try { //將用戶名和密碼通過token傳給shiro進行認證 subject.login(token); TestUser user = (TestUser) subject.getPrincipal(); subject.getSession().setAttribute("testUser", user); return "index"; } catch (UnknownAccountException e) { e.printStackTrace(); model.addAttribute("msg", "用戶名不存在"); return "login"; } catch (IncorrectCredentialsException e) { e.printStackTrace(); model.addAttribute("msg", "密碼有誤"); return "login"; } } @ResponseBody @GetMapping("/unauthor") public String unauthor() { return "權限不足,無法訪問"; } @GetMapping("/logout") public String logout() { Subject subject = SecurityUtils.getSubject(); subject.logout(); return "login"; } @PostMapping("/register") public String register(TestUser testUser, Model model) { String username = testUser.getUsername(); String password = testUser.getPassword(); if (username ** null || username.equals("")) { model.addAttribute("msg", "用戶名不能為空"); return "register"; } else if (password ** null || password.equals("")) { model.addAttribute("msg", "密碼不能為空"); return "register"; } else if (testUserServer.selectOneByName(username) != null) { model.addAttribute("msg", "用戶名已被占用"); return "register"; } else { testUserServer.insert(testUser); return "login"; } } } -
5.前端頁面
-
(1)index.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymrleaf.org/thymeleaf-extras-shiro"> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="shortcut icon" href="#"/> </head> <body> <div th:if="${session.testUser != null}"> <span th:text="'歡迎回來 '+${session.testUser.username}+'! '"> </span><a href="/logout">退出</a> </div> <a href="/main">main</a> <span shiro:hasPermission="manage"> | <a href="/manage">manage</a></span> <span shiro:hasRole="admin"> | <a href="/admin">admin</a></span> <br> </body> </html> -
(2)login.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="shortcut icon" href="#"/> </head> <body> <form action="/login" method="post"> <span th:text="${msg}" style="color: red"></span> <table> <tr> <td>用戶名:</td> <td><input type="text" name="username"/></td> </tr> <tr> <td>密碼:</td> <td><input type="password" name="password"/></td> </tr> <tr> <td><input type="submit" value="登錄"/></td> <td><a href="/register"> <button type="button" value="注冊">注冊</button> </a> </td> </tr> </table> </form> </body> </html> -
(3)register.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="shortcut icon" href="#"/> </head> <body> <form action="/register" method="post"> <span th:text="${msg}" style="color: red"></span> <table> <tr> <td>用戶名:</td> <td><input type="text" name="username"/></td> </tr> <tr> <td>密碼:</td> <td><input type="password" name="password"/></td> </tr> <tr> <td><input type="submit" value="注冊"/></td> </tr> </table> </form> </body> </html> -
(4)main.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="shortcut icon" href="#"/> </head> <body> <h1>main</h1> </body> </html> -
(5)manage.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="shortcut icon" href="#"/> </head> <body> <h1>manage</h1> </body> </html> -
(6)admin.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="shortcut icon" href="#"/> </head> <body> <h1>admin</h1> </body> </html>
-
-
6.數據庫文件
/* Navicat MySQL Data Transfer Source Server : sunyue Source Server Version : 50724 Source Host : localhost:3306 Source Database : shiro Target Server Type : MYSQL Target Server Version : 50724 File Encoding : 65001 Date: 2021-01-11 22:00:47 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for test_user -- ---------------------------- DROP TABLE IF EXISTS `test_user`; CREATE TABLE `test_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(120) DEFAULT NULL, `password` varchar(120) DEFAULT NULL, `perms` varchar(120) DEFAULT NULL, `role` varchar(120) DEFAULT NULL, `salt` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of test_user -- ---------------------------- INSERT INTO `test_user` VALUES ('4', 'admin', '4867df2e009d0096c4cd8d9be8cc104c', 'manage', 'admin', 'GQR2m1N1o3nSLjtOzMITRQ**'); INSERT INTO `test_user` VALUES ('5', 'user', '636502f40cf197dd2f4b19f56f475b24', '', '', 'Kxw3HZiFmgnlUu8fmjMY7Q**'); INSERT INTO `test_user` VALUES ('6', 'user1', '43f3133aa7e0ef9cf8373521dff8d8e8', 'manage', null, 'J8fn4HpauvNOrlUaRl/Spg**'); INSERT INTO `test_user` VALUES ('7', '1', '1', 'manage', null, null);

浙公網安備 33010602011771號