旅游网后台管理系统(三)权限操作
文章目录
- 1. 创建表
- 1.1 表之间的关系
- 1.2 用户表
- 1.3 角色表
- 1.4 用户与角色的中间表
- 1.5 权限表
- 1.6 角色与权限的中间表
- 2. Spring Security
- 2.1 Spring Security 介绍
- 2.2 Spring Security 快速入门
- 2.3 使用自定义页面
- 2.4 Spring Security 使用数据库认证
- 3. 用户操作
- 3.1 用户登录与退出
- 3.1.1 用户登录流程分析
- 3.1.2 实现用户登录操作
- 3.1.3 实现用户退出操作
- 3.2 查询所有用户
- 3.2.1 查询所有用户流程分析
- 3.2.2 实现查询所有用户操作
- 3.3 添加用户
- 3.3.1 添加用户流程分析
- 3.3.2 实现添加用户操作
- 3.3.3 解决密码加密后用户登录问题
- 3.4 查询用户详情
- 3.4.1 查询用户详情流程分析
- 3.4.2 实现查询用户详情操作
- 4. 角色操作
- 4.1 查询所有角色
- 4.1.1 查询所有角色流程分析
- 4.1.2 实现查询所有角色操作
- 4.2 添加角色
- 4.2.1 添加角色流程分析
- 4.2.2 实现添加角色操作
- 4.3 查询角色详情
- 4.3.1 实现查询角色详情操作
- 4.4 删除角色
- 4.4.1 实现删除角色操作
- 5. 权限操作
- 5.1 查询所有权限
- 5.1.1 实现查询所有权限操作
- 5.2 添加权限
- 5.2.1 实现添加权限操作
- 5.3 删除权限
- 5.3.1 实现删除权限操作
- 6. 权限关联与控制
- 6.1 用户关联角色
- 6.1.1 用户关联角色流程分析
- 6.1.2 实现用户关联角色操作
- 6.2 角色关联权限
- 6.2.1 角色关联权限流程分析
- 6.2.2 实现角色关联权限操作
- 6.3 服务器端方法级权限控制
- 6.3.1 JSR-250 注解的使用
- 6.4 页面端权限控制
- 6.4.1 authentication 标签
- 6.4.2 authorize 标签
1. 创建表
1.1 表之间的关系
- 用户表与角色表之间的关系:多对多
- 角色表与权限表之间的关系:多对多
1.2 用户表
序号 | 字段名称 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | int(11) | 主键,自增长 |
2 | varchar(50) | 邮箱,非空,唯一 | |
3 | userName | varchar(50) | 用户名 |
4 | password | varchar(100) | 密码 |
5 | phoneNum | varchar(50) | 电话 |
6 | status | int(11) | 状态(0未开启 1开启) |
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (`id` int(11) NOT NULL AUTO_INCREMENT,`email` varchar(50) NOT NULL,`userName` varchar(50) DEFAULT NULL,`password` varchar(100) DEFAULT NULL,`phoneNum` varchar(50) DEFAULT NULL,`status` int(11) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;INSERT INTO `users` VALUES ('1', '123@163.com', '张三', '$2a$10$7u0aq7hSJ8xFNEFXESRgfObOZw7YxV.YVHNOiMentENOIForpkQzS', '13333333333', '1');
INSERT INTO `users` VALUES ('2', '321@163.com', '李四', '$2a$10$7u0aq7hSJ8xFNEFXESRgfObOZw7YxV.YVHNOiMentENOIForpkQzS', '12222222222', '1');
1.3 角色表
序号 | 字段名称 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | int(11) | 主键,自增长 |
2 | roleName | varchar(50) | 用户名 |
3 | roleDesc | varchar(50) | 用户描述 |
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (`id` int(11) NOT NULL AUTO_INCREMENT,`roleName` varchar(50) DEFAULT NULL,`roleDesc` varchar(50) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;INSERT INTO `role` VALUES ('1', 'USER', '用户');
INSERT INTO `role` VALUES ('2', 'ADMIN', '管理员');
1.4 用户与角色的中间表
序号 | 字段名称 | 字段类型 | 字段描述 |
---|---|---|---|
1 | usersId | int(11) | 用户 id(外键) |
2 | roleId | int(11) | 角色 id(外键) |
DROP TABLE IF EXISTS `users_role`;
CREATE TABLE `users_role` (`usersId` int(11) NOT NULL,`roleId` int(11) NOT NULL,PRIMARY KEY (`usersId`,`roleId`),KEY `roleId` (`roleId`),CONSTRAINT `users_role_ibfk_1` FOREIGN KEY (`usersId`) REFERENCES `users` (`id`),CONSTRAINT `users_role_ibfk_2` FOREIGN KEY (`roleId`) REFERENCES `role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO `users_role` VALUES ('1', '1');
INSERT INTO `users_role` VALUES ('2', '2');
1.5 权限表
序号 | 字段名称 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | int(11) | 主键,自增长 |
2 | permissionName | varchar(50) | 权限名 |
3 | url | varchar(50) | 资源路径 |
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (`id` int(11) NOT NULL AUTO_INCREMENT,`permissionName` varchar(50) DEFAULT NULL,`url` varchar(50) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;INSERT INTO `permission` VALUES ('1', 'user', '/user/*.do');
INSERT INTO `permission` VALUES ('2', 'product', '/product/*.do');
INSERT INTO `permission` VALUES ('3', 'orders', '/orders/*.do');
INSERT INTO `permission` VALUES ('4', 'role', '/role/*.do');
INSERT INTO `permission` VALUES ('5', 'permission', '/permission/*.do');
INSERT INTO `permission` VALUES ('6', 'sysLog', '/sysLog/*.do');
1.6 角色与权限的中间表
序号 | 字段名称 | 字段类型 | 字段描述 |
---|---|---|---|
1 | permissionId | int(11) | 权限 id(外键) |
2 | roleId | int(11) | 角色 id(外键) |
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission` (`permissionId` int(11) NOT NULL,`roleId` int(11) NOT NULL,PRIMARY KEY (`permissionId`,`roleId`),KEY `roleId` (`roleId`),CONSTRAINT `role_permission_ibfk_1` FOREIGN KEY (`permissionId`) REFERENCES `permission` (`id`),CONSTRAINT `role_permission_ibfk_2` FOREIGN KEY (`roleId`) REFERENCES `role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO `role_permission` VALUES ('2', '1');
INSERT INTO `role_permission` VALUES ('3', '1');
INSERT INTO `role_permission` VALUES ('1', '2');
INSERT INTO `role_permission` VALUES ('2', '2');
INSERT INTO `role_permission` VALUES ('3', '2');
INSERT INTO `role_permission` VALUES ('4', '2');
INSERT INTO `role_permission` VALUES ('5', '2');
INSERT INTO `role_permission` VALUES ('6', '2');
2. Spring Security
2.1 Spring Security 介绍
Spring Security 是 Spring 项目组中用来提供安全认证服务的框架。
安全包括两个主要操作:
- 认证:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。比如登陆时,校验用户名密码是否正确来完成认证过程。
- 授权:用户授权指的是验证某个用户是否有权限执行某个操作
2.2 Spring Security 快速入门
创建一个 maven 项目,骨架选择 web-app
导入依赖坐标
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId><version>${spring.security.version}</version> </dependency> <dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId><version>${spring.security.version}</version> </dependency>
配置 web.xml
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-security.xml</param-value> </context-param> <listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <filter><filter-name>springSecurityFilterChain</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping><filter-name>springSecurityFilterChain</filter-name><url-pattern>/*</url-pattern> </filter-mapping>
配置 spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:security="http://www.springframework.org/schema/security"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security.xsd"><security:http auto-config="true" use-expressions="false"><!-- intercept-url定义一个过滤规则 pattern表示对哪些 url 进行权限控制,access 属性表示在请求对应的 URL 时需要什么权限 --><security:intercept-url pattern="/**" access="ROLE_USER"/></security:http><security:authentication-manager><security:authentication-provider><security:user-service><!--配置用户信息--><security:user name="user" password="{noop}user" authorities="ROLE_USER"/><security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN"/></security:user-service></security:authentication-provider></security:authentication-manager> </beans>
测试
启动项目
tomcat7:run
打开浏览器,输入
http://localhost:8090
自动进入登陆页面(因为我们配置了 auto-config=”true”,Spring Security 自动为我们生成了登陆页面)
http://localhost:8090/login
输入以下内容,进入 index.jsp
User:user Password:user
输入以下内容,报 403 错误(权限不够访问)
User:admin Password:admin
输入其他内容,页面给出提示
User:123 Password:123
2.3 使用自定义页面
修改 spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:security="http://www.springframework.org/schema/security"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security.xsd"><!-- 配置不过滤的资源(静态资源及登录相关) --><security:http security="none" pattern="/login.html"/><security:http security="none" pattern="/success.html"/><security:http security="none" pattern="/failer.html"/><security:http auto-config="true" use-expressions="false"><!-- intercept-url定义一个过滤规则 pattern表示对哪些 url 进行权限控制,access 属性表示在请求对应的 URL 时需要什么权限 --><security:intercept-url pattern="/**" access="ROLE_USER"/><!-- 自定义登陆页面login-page 自定义登陆页面,authentication-failure-url 用户权限校验失败之后才会跳转到这个页面,default-target-url 登陆成功后跳转的页面。 --><security:form-login login-page="/login.html"login-processing-url="/login"username-parameter="username" password-parameter="password"authentication-failure-url="/failer.html"default-target-url="/success.html"/><!-- 关闭 CSRF ,默认是开启的 --><security:csrf disabled="true"/></security:http><security:authentication-manager><security:authentication-provider><security:user-service><!--配置用户信息--><security:user name="user" password="{noop}user" authorities="ROLE_USER"/><security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN"/></security:user-service></security:authentication-provider></security:authentication-manager> </beans>
编写 login.html
<!DOCTYPE html> <html> <head><meta charset="UTF-8"><title>Insert title here</title></head> <body> <form action="login" method="post"><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 colspan="2" align="center"><input type="submit" value="登录"/><input type="reset" value="重置"/></td></tr></table> </form> </body> </html>
编写 success.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h2>success</h2> </body> </html>
编写 failer.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h2>failer</h2> </body> </html>
测试
启动项目
tomcat7:run
打开浏览器,输入
http://localhost:8090
自动进入 login.html
http://localhost:8090/login.html
输入以下内容,进入 success.html
User:user Password:user
输入以下内容,进入 success.html
User:admin Password:admin
输入其他内容,进入 failer.html
User:123 Password:123
2.4 Spring Security 使用数据库认证
在 Spring Security 中如果想要使用数据进行认证操作,有很多种操作方式,这里我们介绍使用 UserDetails,UserDetailsService 来完成操作。
UserDetails
UserDetails 是一个接口,作用是封装当前进行认证的用户信息,但由于其是一个接口,所以我们可以对其进行实现
UserDetailsService
UserDetailsService 是一个接口,作用是规范用于认证的方法,但由于其是一个接口,所以我们可以对其进行实现
3. 用户操作
3.1 用户登录与退出
3.1.1 用户登录流程分析
3.1.2 实现用户登录操作
导入 Spring Security 依赖
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId><version>${spring.security.version}</version> </dependency> <dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId><version>${spring.security.version}</version> </dependency> <dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-core</artifactId><version>${spring.security.version}</version> </dependency> <dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-taglibs</artifactId><version>${spring.security.version}</version> </dependency>
配置 web.xml
<!-- 配置 Spring 的监听器 --> <listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 设置配置文件的路径 --> <context-param><param-name>contextConfigLocation</param-name><param-value>classpath*:spring-security.xml</param-value> </context-param><!-- 配置 spring-security --> <filter><filter-name>springSecurityFilterChain</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping><filter-name>springSecurityFilterChain</filter-name><url-pattern>/*</url-pattern> </filter-mapping>
配置 spring-security.xml(相当于代替了控制层的代码)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:security="http://www.springframework.org/schema/security"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security.xsd"><!-- 配置不拦截的资源 --><security:http pattern="/login.jsp" security="none"/><security:http pattern="/failer.jsp" security="none"/><security:http pattern="/css/**" security="none"/><security:http pattern="/img/**" security="none"/><security:http pattern="/plugins/**" security="none"/><!-- 配置具体的规则auto-config="true" 不用自己编写登录的页面,框架提供默认登录页面use-expressions="false" 是否使用 SPEL 表达式(没学习过)--><security:http auto-config="true" use-expressions="false"><!-- 配置具体的拦截的规则 pattern="请求路径的规则" access="访问系统的人,必须有 ROLE_USER 或者 ROLE_ADMIN 的角色" --><security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN"/><!-- 定义跳转的具体的页面 --><security:form-loginlogin-page="/login.jsp"login-processing-url="/login.do"default-target-url="/index.jsp"authentication-failure-url="/failer.jsp"authentication-success-forward-url="/pages/main.jsp"/><!-- 关闭跨域请求 --><security:csrf disabled="true"/><!-- 退出 --><security:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/login.jsp" /></security:http><!-- 切换成数据库中的用户名和密码 --><security:authentication-manager><security:authentication-provider user-service-ref="userService"> </security:authentication-provider></security:authentication-manager></beans>
编写实现类
/*** 用户实体类*/ public class UserInfo implements Serializable {private Integer id; // 主键private String email; // 邮箱private String username; // 用户名private String password; // 密码private String phoneNum; // 电话private Integer status; // 状态(0未开启 1开启)private String statusStr; // 状态字符串private List<Role> roles; // 角色public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getPhoneNum() {return phoneNum;}public void setPhoneNum(String phoneNum) {this.phoneNum = phoneNum;}public Integer getStatus() {return status;}public void setStatus(Integer status) {this.status = status;}public String getStatusStr() {if(status != null){if(status == 0){statusStr = "未开启";}if(status == 1){statusStr = "开启";}}return statusStr;}public void setStatusStr(String statusStr) {this.statusStr = statusStr;}public List<Role> getRoles() {return roles;}public void setRoles(List<Role> roles) {this.roles = roles;}@Overridepublic String toString() {return "UserInfo{" +"id=" + id +", email='" + email + '\'' +", username='" + username + '\'' +", password='" + password + '\'' +", phoneNum='" + phoneNum + '\'' +", status=" + status +", statusStr='" + statusStr + '\'' +", roles=" + roles +'}';} }
/*** 角色实体类*/ public class Role implements Serializable {private Integer id; // 主键private String roleName; //角色名private String roleDesc; // 角色描述private List<Permission> permissions; // 权限private List<UserInfo> users; // 角色public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getRoleName() {return roleName;}public void setRoleName(String roleName) {this.roleName = roleName;}public String getRoleDesc() {return roleDesc;}public void setRoleDesc(String roleDesc) {this.roleDesc = roleDesc;}public List<Permission> getPermissions() {return permissions;}public void setPermissions(List<Permission> permissions) {this.permissions = permissions;}public List<UserInfo> getUsers() {return users;}public void setUsers(List<UserInfo> users) {this.users = users;}@Overridepublic String toString() {return "Role{" +"id=" + id +", roleName='" + roleName + '\'' +", roleDesc='" + roleDesc + '\'' +", permissions=" + permissions +", users=" + users +'}';} }
/*** 权限实体类*/ public class Permission implements Serializable {private Integer id; // 主键private String permissionName; // 权限名private String url; // 资源路径private List<Role> roles; // 角色public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getPermissionName() {return permissionName;}public void setPermissionName(String permissionName) {this.permissionName = permissionName;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public List<Role> getRoles() {return roles;}public void setRoles(List<Role> roles) {this.roles = roles;}@Overridepublic String toString() {return "Permission{" +"id=" + id +", permissionName='" + permissionName + '\'' +", url='" + url + '\'' +", roles=" + roles +'}';} }
编写持久层接口
@Repository public interface UserDao {/*** 根据用户名查询用户信息* @param userName* @return* @throws Exception*/@Select("select * from users where userName = #{userName}")@Results({@Result(id = true, property = "id", column = "id"),@Result(property = "email", column = "email"),@Result(property = "username", column = "username"),@Result(property = "password", column = "password"),@Result(property = "phoneNum", column = "phoneNum"),@Result(property = "status", column = "status"),@Result(property = "roles", column = "id", many = @Many(select = "com.zt.dao.RoleDao.findById"))})UserInfo findByUsername(String userName) throws Exception; }
public interface RoleDao {/*** 根据用户 ID 查询用户角色** @param id* @return*/@Select("select * from role where id in (select roleId from users_role where usersId = #{id})")List<Role> findById(Integer id) throws Exception; }
编写业务层接口和实现类
public interface UserService extends UserDetailsService {}
@Service("userService") public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {UserInfo userInfo = null;try {userInfo = userDao.findByUsername(username);} catch (Exception e) {e.printStackTrace();}// 将自己的用户对象封装为一个 UserDetailsUser user = new User(userInfo.getUsername(),"{noop}" + userInfo.getPassword(),userInfo.getStatus() == 1, true, true, true,getAuthorities(userInfo.getRoles()));return user;}// 返回一个 List 集合,集合中装的是角色信息public List<SimpleGrantedAuthority> getAuthorities(List<Role> roles){ArrayList<SimpleGrantedAuthority> authorities = new ArrayList<>();for (Role role : roles) {authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getRoleName()));}return authorities;}}
在 webapp 下添加 login.jsp,failer.jsp
3.1.3 实现用户退出操作
配置 spring-security.xml
<!-- 退出 --> <security:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/login.jsp" />
给注销按钮链接赋值
${pageContext.request.contextPath}/logout.do
3.2 查询所有用户
3.2.1 查询所有用户流程分析
3.2.2 实现查询所有用户操作
编写持久层接口
package com.zt.dao;import com.zt.domain.UserInfo; import org.apache.ibatis.annotations.Many; import org.apache.ibatis.annotations.Result; import org.apache.ibatis.annotations.Results; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository;import java.util.List;@Repository public interface UserDao {/*** 查询所有用户信息* @return* @throws Exception*/@Select("select * from users")List<UserInfo> findAll() throws Exception; }
编写业务层接口和实现类
public interface UserService extends UserDetailsService {/*** 查询所有用户* @return* @throws Exception*/List<UserInfo> findAll() throws Exception; }
@Service("userService") public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;/*** 查询所有用户* @return* @throws Exception*/@Overridepublic List<UserInfo> findAll() throws Exception {return userDao.findAll();} }
编写控制层
@Controller @RequestMapping("/user") public class UserController {@Autowiredprivate UserService userService;/*** 查询所有用户* @return* @throws Exception*/@RequestMapping("/findAll")public ModelAndView findAll() throws Exception{ModelAndView modelAndView = new ModelAndView();List<UserInfo> users = userService.findAll();modelAndView.addObject("userList",users);modelAndView.setViewName("user-list");return modelAndView;} }
编写 JSP 页面
在 pages 包中创建 user-list.jsp
3.3 添加用户
3.3.1 添加用户流程分析
3.3.2 实现添加用户操作
配置 spring-security.xml
<!-- 配置加密类 --> <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
编写持久层接口
package com.zt.dao;import com.zt.domain.UserInfo; import org.apache.ibatis.annotations.*; import org.springframework.stereotype.Repository;import java.util.List;@Repository public interface UserDao {/*** 添加用户* @param userInfo* @throws Exception*/@Insert("insert into users(email,userName,password,phoneNum,status) values(#{email},#{username},#{password},#{phoneNum},#{status})")void save(UserInfo userInfo) throws Exception; }
编写业务层接口和实现类
public interface UserService extends UserDetailsService {/*** 添加用户* @param userInfo* @throws Exception*/void save(UserInfo userInfo) throws Exception; }
@Service("userService") public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Autowiredprivate PasswordEncoder passwordEncoder;/*** 添加用户** @param userInfo* @throws Exception*/@Overridepublic void save(UserInfo userInfo) throws Exception {// 对密码进行加密userInfo.setPassword(passwordEncoder.encode(userInfo.getPassword()));userDao.save(userInfo);} }
编写控制层
@Controller @RequestMapping("/user") public class UserController {@Autowiredprivate UserService userService;/*** 添加用户* @param userInfo* @return* @throws Exception*/@RequestMapping("/save.do")public String save(UserInfo userInfo) throws Exception{userService.save(userInfo);return "redirect:findAll.do";} }
编写 JSP 页面
在 pages 包中创建 user-add.jsp
3.3.3 解决密码加密后用户登录问题
提出问题
添加用户后,由于密码进行了加密,当我们用添加的用户进行登录时,登陆失败了
分析问题
这是因为我们数据库中存储的密码是加密后的,我们读取时并没有对其解密,所以与输入密码不一致
解决问题
配置 spring-security.xml
<!-- 切换成数据库中的用户名和密码 --> <security:authentication-manager><security:authentication-provider user-service-ref="userService"><!-- 配置加密的方式 --><security:password-encoder ref="passwordEncoder"/></security:authentication-provider> </security:authentication-manager>
删除 UserServiceImpl 中的 “{noop}”
@Service("userService") public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {UserInfo userInfo = null;try {userInfo = userDao.findByUsername(username);} catch (Exception e) {e.printStackTrace();}// 将自己的用户对象封装为一个 UserDetailsUser user = new User(userInfo.getUsername(),userInfo.getPassword(),userInfo.getStatus() == 1, true, true, true,getAuthorities(userInfo.getRoles()));return user;}// 返回一个 List 集合,集合中装的是角色信息public List<SimpleGrantedAuthority> getAuthorities(List<Role> roles) {ArrayList<SimpleGrantedAuthority> authorities = new ArrayList<>();for (Role role : roles) {authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName()));}return authorities;}}
3.4 查询用户详情
3.4.1 查询用户详情流程分析
3.4.2 实现查询用户详情操作
编写持久层接口
@Repository public interface UserDao {/*** 查询单个用户详细信息** @param id* @return* @throws Exception*/@Select("select * from users where id=#{id}")@Results({@Result(id = true, property = "id", column = "id"),@Result(property = "email", column = "email"),@Result(property = "username", column = "username"),@Result(property = "password", column = "password"),@Result(property = "phoneNum", column = "phoneNum"),@Result(property = "status", column = "status"),@Result(property = "roles", column = "id", many = @Many(select = "com.zt.dao.RoleDao.findById"))})UserInfo findById(Integer id) throws Exception; }
@Repository public interface RoleDao {/*** 根据用户 ID 查询角色** @param id* @return*/@Select("select * from role where id in (select roleId from users_role where usersId = #{id})")@Results({@Result(id = true, property = "id", column = "id"),@Result(property = "roleName", column = "roleName"),@Result(property = "roleDesc", column = "roleDesc"),@Result(property = "permissions", column ="id",many = @Many(select = "com.zt.dao.PermissionDao.findById"))})List<Role> findById(Integer id) throws Exception; }
@Repository public interface PermissionDao {/*** 根据角色 ID 查询权限* @param id* @return* @throws Exception*/@Select("select * from permission where id in (select permissionId from role_permission where roleId = #{id})")List<Permission> findById(Integer id) throws Exception; }
编写业务层接口和实现类
public interface UserService extends UserDetailsService {/*** 查询单个用户详细信息* @param id* @return* @throws Exception*/UserInfo findById(Integer id) throws Exception; }
@Service("userService") public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;/*** 查询单个用户详细信息* @param id* @return* @throws Exception*/@Overridepublic UserInfo findById(Integer id) throws Exception {return userDao.findById(id);} }
编写控制层
@Controller @RequestMapping("/user") public class UserController {@Autowiredprivate UserService userService;/*** 查询单个用户详细信息* @param id* @return* @throws Exception*/@RequestMapping("/findById.do")public ModelAndView findById(Integer id) throws Exception{ModelAndView modelAndView = new ModelAndView();UserInfo user = userService.findById(id);modelAndView.addObject("user",user);modelAndView.setViewName("user-show");return modelAndView;} }
编写 JSP 页面
在 pages 包中创建 user-show.jsp
4. 角色操作
4.1 查询所有角色
4.1.1 查询所有角色流程分析
4.1.2 实现查询所有角色操作
编写持久层接口
@Repository public interface RoleDao {/*** 查询所有角色* @return* @throws Exception*/@Select("select * from role")List<Role> findAll() throws Exception; }
编写业务层接口和实现类
public interface RoleService {/*** 查询所有角色* @return* @throws Exception*/List<Role> findAll() throws Exception; }
@Transactional @Service("roleService") public class RoleServiceImpl implements RoleService {@Autowiredprivate RoleDao roleDao;/*** 查询所有角色* @return* @throws Exception*/@Overridepublic List<Role> findAll() throws Exception {return roleDao.findAll();} }
编写控制层
@Controller @RequestMapping("/role") public class RoleController {@Autowiredprivate RoleService roleService;/*** 查询所有角色* @return* @throws Exception*/@RequestMapping("/findAll.do")public ModelAndView findAll() throws Exception {ModelAndView modelAndView = new ModelAndView();List<Role> roleList = roleService.findAll();modelAndView.addObject("roleList",roleList);modelAndView.setViewName("role-list");return modelAndView;} }
编写 JSP 页面
在 pages 包中创建 role-list.jsp
4.2 添加角色
4.2.1 添加角色流程分析
4.2.2 实现添加角色操作
编写持久层接口
@Repository public interface RoleDao {/*** 添加角色* @param role* @throws Exception*/@Insert("insert into role(roleName,roleDesc) values(#{roleName},#{roleDesc})")void save(Role role) throws Exception; }
编写业务层接口和实现类
public interface RoleService {/*** 添加角色* @param role* @throws Exception*/void save(Role role) throws Exception; }
@Transactional @Service("roleService") public class RoleServiceImpl implements RoleService {@Autowiredprivate RoleDao roleDao;/*** 添加角色* @param role* @throws Exception*/@Overridepublic void save(Role role) throws Exception {roleDao.save(role);} }
编写控制层
@Controller @RequestMapping("/role") public class RoleController {@Autowiredprivate RoleService roleService;/*** 添加角色* @param role* @return* @throws Exception*/@RequestMapping("/save.do")public String save(Role role) throws Exception {roleService.save(role);return "redirect:findAll.do";} }
编写 JSP 页面
在 pages 包中创建 user-add.jsp
4.3 查询角色详情
4.3.1 实现查询角色详情操作
编写持久层接口
@Repository public interface RoleDao {/*** 查询角色详情** @param id* @return*/@Select("select * from role where id=#{id}")@Results({@Result(id = true, property = "id", column = "id"),@Result(property = "roleName",column = "roleName"),@Result(property = "roleDesc",column = "roleDesc"),@Result(property = "permissions", column = "id", many = @Many(select = "com.zt.dao.PermissionDao.findById"))})Role findByRoleId(Integer id) throws Exception; }
编写业务层接口和实现类
public interface RoleService {/*** 查询角色详情* @param id* @return* @throws Exception*/Role findById(Integer id) throws Exception; }
@Transactional @Service("roleService") public class RoleServiceImpl implements RoleService {@Autowiredprivate RoleDao roleDao;/*** 查询角色详情* @param id* @return* @throws Exception*/@Overridepublic Role findById(Integer id) throws Exception {return roleDao.findByRoleId(id);} }
编写控制层
@Controller @RequestMapping("/role") public class RoleController {@Autowiredprivate RoleService roleService;/*** 查询角色详情* @param id* @return* @throws Exception*/@RequestMapping("/findById.do")public ModelAndView findById(Integer id) throws Exception {ModelAndView modelAndView = new ModelAndView();Role role = roleService.findById(id);modelAndView.addObject("role",role);modelAndView.setViewName("role-show");return modelAndView;} }
编写 JSP 页面
在 pages 包中创建 role-show.jsp
4.4 删除角色
4.4.1 实现删除角色操作
编写持久层接口
@Repository public interface RoleDao {/*** 删除角色所拥有的所有权限* @param id* @throws Exception*/@Delete("delete from role_permission where roleId = #{id}")void deleteFromRole_PermissionByRoleId(Integer id) throws Exception;/*** 删除用户所拥有的这种角色* @param id* @throws Exception*/@Delete("delete from users_role where roleId = #{id}")void deleteFromUsers_RoleByRoleId(Integer id) throws Exception;/*** 删除角色* @param id* @throws Exception*/@Delete("delete from role where id = #{id}")void deleteRole(Integer id) throws Exception; }
编写业务层接口和实现类
public interface RoleService {/*** 删除角色* @param id* @throws Exception*/void deleteRole(Integer id) throws Exception; }
@Transactional @Service("roleService") public class RoleServiceImpl implements RoleService {@Autowiredprivate RoleDao roleDao;/*** 删除角色* @param id* @throws Exception*/@Overridepublic void deleteRole(Integer id) throws Exception {roleDao.deleteFromRole_PermissionByRoleId(id);roleDao.deleteFromUsers_RoleByRoleId(id);roleDao.deleteRole(id);} }
编写控制层
@Controller @RequestMapping("/role") public class RoleController {@Autowiredprivate RoleService roleService;/*** 删除角色* @param id* @return* @throws Exception*/@RequestMapping("/deleteRole.do")public String deleteRole(Integer id) throws Exception {roleService.deleteRole(id);return "redirect:findAll.do";} }
5. 权限操作
5.1 查询所有权限
5.1.1 实现查询所有权限操作
编写持久层接口
@Repository public interface PermissionDao {/*** 查询所有权限* @return* @throws Exception*/@Select("select * from permission")List<Permission> findAll() throws Exception; }
编写业务层接口和实现类
public interface PermissionService {/*** 查询所有权限* @return* @throws Exception*/List<Permission> findAll() throws Exception; }
@Transactional @Service("permissionService") public class PermissionServiceImpl implements PermissionService {@Autowiredprivate PermissionDao permissionDao;/*** 查询所有权限* @return* @throws Exception*/@Overridepublic List<Permission> findAll() throws Exception {return permissionDao.findAll();} }
编写控制层
@Controller @RequestMapping("/permission") public class PermissionController {@Autowiredprivate PermissionService permissionService;/*** 查询所有权限* @return* @throws Exception*/@RequestMapping("/findAll.do")public ModelAndView findAll() throws Exception {ModelAndView modelAndView = new ModelAndView();List<Permission> permissionList = permissionService.findAll();modelAndView.addObject("permissionList",permissionList);modelAndView.setViewName("permission-list");return modelAndView;} }
编写 JSP 页面
在 pages 包中创建 permission-list.jsp
5.2 添加权限
5.2.1 实现添加权限操作
编写持久层接口
@Repository public interface PermissionDao {/*** 添加权限* @param permission* @throws Exception*/@Insert("insert into permission(permissionName,url) values(#{permissionName},#{url})")void save(Permission permission) throws Exception; }
编写业务层接口和实现类
public interface PermissionService {/*** 添加权限* @throws Exception*/void save(Permission permission) throws Exception; }
@Transactional @Service("permissionService") public class PermissionServiceImpl implements PermissionService {@Autowiredprivate PermissionDao permissionDao;/*** 添加权限* @param permission* @throws Exception*/@Overridepublic void save(Permission permission) throws Exception {permissionDao.save(permission);} }
编写控制层
@Controller @RequestMapping("/permission") public class PermissionController {@Autowiredprivate PermissionService permissionService;/*** 添加权限* @param permission* @return* @throws Exception*/@RequestMapping("/save.do")public String save(Permission permission) throws Exception {permissionService.save(permission);return "redirect:findAll.do";} }
编写 JSP 页面
在 pages 包中创建 permission-add.jsp
5.3 删除权限
5.3.1 实现删除权限操作
编写持久层接口
@Repository public interface PermissionDao {/*** 删除角色所拥有的该权限* @param id*/@Delete("delete from role_permission where permissionId = #{id}")void deleteFromRole_PermissionByPermissionId(Integer id) throws Exception;/*** 删除权限* @param id*/@Delete("delete from permission where id = #{id}")void deletePermission(Integer id) throws Exception; }
编写业务层接口和实现类
public interface PermissionService {/*** 删除权限* @param id* @throws Exception*/void deletePermission(Integer id) throws Exception; }
@Transactional @Service("permissionService") public class PermissionServiceImpl implements PermissionService {@Autowiredprivate PermissionDao permissionDao;/*** 删除权限* @param id* @throws Exception*/@Overridepublic void deletePermission(Integer id) throws Exception {permissionDao.deleteFromRole_PermissionByPermissionId(id);permissionDao.deletePermission(id);} }
编写控制层
@Controller@RequestMapping("/permission")public class PermissionController {@Autowiredprivate PermissionService permissionService;/*** 删除权限* @param id* @return* @throws Exception*/@RequestMapping("/deletePermission.do")public String deletePermission(Integer id) throws Exception {permissionService.deletePermission(id);return "redirect:findAll.do";}}
6. 权限关联与控制
6.1 用户关联角色
6.1.1 用户关联角色流程分析
- 先查询出这个用户没有的角色信息
- 给用户添加角色就是向 users_role 插入数据
6.1.2 实现用户关联角色操作
编写持久层接口
@Repository public interface UserDao {/*** 获取可以添加的角色信息** @param id* @return*/@Select("select * from role where id not in (select roleId from users_role where usersId = #{id})")List<Role> findOtherRole(Integer id) throws Exception;/*** 给用户添加角色** @param userId* @param id* @throws Exception*/@Insert("insert into users_role values(#{userId},#{id})")void addRoleToUser(@Param("userId") Integer userId, @Param("id") Integer id) throws Exception; }
编写业务层接口和实现类
public interface UserService extends UserDetailsService {/*** 获取可以添加的角色信息* @param id* @return* @throws Exception*/List<Role> findOtherRole(Integer id) throws Exception;/*** 给用户添加角色* @param userId* @param ids*/void addRoleToUser(Integer userId, Integer[] ids) throws Exception; }
@Transactional @Service("userService") public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;/*** 获取可以添加的角色信息** @param id* @return* @throws Exception*/@Overridepublic List<Role> findOtherRole(Integer id) throws Exception {return userDao.findOtherRole(id);}/*** 给用户添加角色** @param userId* @param ids*/@Overridepublic void addRoleToUser(Integer userId, Integer[] ids) throws Exception{for (Integer id : ids) {userDao.addRoleToUser(userId, id);}} }
编写控制层
@Controller @RequestMapping("/user") public class UserController {@Autowiredprivate UserService userService;/*** 查找要添加角色的用户,及可以添加的角色** @param id 用户 id* @return* @throws Exception*/@RequestMapping("/findUserByIdAndAllRole.do")public ModelAndView findUserByIdAndAllRole(Integer id) throws Exception {ModelAndView modelAndView = new ModelAndView();UserInfo user = userService.findById(id);List<Role> roleList = userService.findOtherRole(id);modelAndView.addObject("user", user);modelAndView.addObject("roleList", roleList);modelAndView.setViewName("user-role-add");return modelAndView;}/*** 给用户添加角色* @param userId* @param ids* @return*/@RequestMapping("/addRoleToUser.do")public String addRoleToUser(Integer userId, Integer[] ids) throws Exception {userService.addRoleToUser(userId, ids);return "redirect:findAll.do";} }
编写 JSP 页面
在 pages 包中创建 user-role-add.jsp
6.2 角色关联权限
6.2.1 角色关联权限流程分析
- 先查询出这个角色没有的权限信息
- 给角色添加权限就是向 role_permission 插入数据
6.2.2 实现角色关联权限操作
编写持久层接口
@Repository public interface RoleDao {/*** 获取可以添加的权限信息** @param id* @return* @throws Exception*/@Select("select * from permission where id not in (select permissionId from role_permission where roleId = #{id})")List<Permission> findOtherPermission(Integer id) throws Exception;/*** 给角色添加权限** @param roleId* @param id*/@Insert("insert into role_permission values(#{id}, #{roleId})")void addPermissionToRole(@Param("roleId") Integer roleId, @Param("id") Integer id) throws Exception; }
编写业务层接口和实现类
public interface RoleService {/*** 获取可以添加的权限信息* @param id* @return* @throws Exception*/List<Permission> findOtherPermission(Integer id) throws Exception;/*** 给角色添加权限* @param roleId* @param ids* @throws Exception*/void addPermissionToRole(Integer roleId, Integer[] ids) throws Exception; }
@Transactional @Service("roleService") public class RoleServiceImpl implements RoleService {@Autowiredprivate RoleDao roleDao;/*** 获取可以添加的权限信息* @param id* @return* @throws Exception*/@Overridepublic List<Permission> findOtherPermission(Integer id) throws Exception {return roleDao.findOtherPermission(id);}/*** 给角色添加权限* @param roleId* @param ids* @throws Exception*/@Overridepublic void addPermissionToRole(Integer roleId, Integer[] ids) throws Exception {for (Integer id : ids) {roleDao.addPermissionToRole(roleId,id);}} }
编写控制层
@Controller @RequestMapping("/role") public class RoleController {@Autowiredprivate RoleService roleService;/*** 查找要添加权限的角色,及可以添加的权限** @param id* @return*/@RequestMapping("/findRoleByIdAndAllPermission.do")public ModelAndView findRoleByIdAndAllPermission(Integer id) throws Exception {ModelAndView modelAndView = new ModelAndView();Role role = roleService.findById(id);List<Permission> permissionList = roleService.findOtherPermission(id);modelAndView.addObject("role", role);modelAndView.addObject("permissionList", permissionList);modelAndView.setViewName("role-permission-add");return modelAndView;}/*** 给角色添加权限* @param roleId* @param ids* @return* @throws Exception*/@RequestMapping("/addPermissionToRole.do")public String addPermissionToRole(Integer roleId, Integer[] ids) throws Exception {roleService.addPermissionToRole(roleId, ids);return "redirect:findAll.do";} }
编写 JSP 页面
在 pages 包中创建 role-permission-add.jsp
6.3 服务器端方法级权限控制
在服务器端我们可以通过 Spring Security 提供的注解来对方法进行权限控制。Spring Security 在方法的权限控制上支持三种类型的注解,JSR-250 注解、@Secured 注解和支持表达式的注解。
下面介绍 JSR-250 注解的使用,其他两种注解与之类似。
6.3.1 JSR-250 注解的使用
导入 JSR-250 的依赖
<dependency><groupId>javax.annotation</groupId><artifactId>jsr250-api</artifactId><version>1.0</version> </dependency>
在 spring-security.xml 中开启 JSR-250 注解
<!--开启 JSR-250 注解使用--> <security:global-method-security jsr250-annotations="enabled"/>
在 controller 中指定的方法上使用 JSR-250 注解
/*** 查询所有用户** @return* @throws Exception*/ @RequestMapping("/findAll.do") @RolesAllowed("ADMIN") public ModelAndView findAll() throws Exception {ModelAndView modelAndView = new ModelAndView();List<UserInfo> users = userService.findAll();modelAndView.addObject("userList", users);modelAndView.setViewName("user-list");return modelAndView; }
6.4 页面端权限控制
6.4.1 authentication 标签
作用
获取当前正在操作的用户信息
使用步骤
导入依赖
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-taglibs</artifactId><version>${spring.security.version}</version> </dependency>
配置 spring-security.xml
<!--开启表达式的权限控制--> <bean id="webexpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>
在 jsp 页面导入
<%@taglib uri="http://www.springframework.org/security/tags" prefix="security"%>
在 JSP 中获取正在操作的用户信息
<security:authentication property="principal.username"></security:authentication>
6.4.2 authorize 标签
作用
用于控制页面上的标签是否有权限显示
使用步骤
导入依赖
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-taglibs</artifactId><version>${spring.security.version}</version> </dependency>
配置 spring-security.xml
<!--开启表达式的权限控制--> <bean id="webexpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>
在 jsp 页面导入
<%@taglib uri="http://www.springframework.org/security/tags" prefix="security"%>
在 JSP 中控制页面上的标签是否有权限显示
<security:authorize access="hasRole('ADMIN')"></security:authorize>
旅游网后台管理系统(三)权限操作相关推荐
- 旅游网后台管理系统(一)环境搭建
文章目录 1. 系统介绍 2. 技术选型 3. 环境搭建 3.1 创建数据库和表 3.2 创建 Maven 工程 3.2.1 创建父工程 3.2.2 创建子模块 dao 3.2.3 创建子模块 ser ...
- 2021-06-27基于web旅游景点后台管理系统
## 基于web旅游景点后台管理系统 功能模块: 用户模块 登录模块(注册) 景点模块 景点线路查询及周围食宿查询模块 订单模块 可视化模块(大屏和疫情实时可视化) 启动前端项目(npm run de ...
- 后台管理系统怎么实现操作日志原理_springboot角色权限后台管理系统脚手架实战开发教程包含完整源码...
自从猿来入此发布实战开发教程以来,我们截至目前一共发布了22个Java实战项目开发教程,从最基础的Java控制台实战项目到数据库封装教程再到swing的单机项目教程.servlet的web实战教程.s ...
- 动态后台获取_后台管理系统的权限以及vue处理权限的思路
一般来说,在(后台)管理系统(最早的企业级的项目和网站的后台管理系统现在大部分人都叫后台管理系统)中才会有权限之说.权限分为功能级权限和数据级权限.这篇文章主要谈论功能级权限. 一.名词解释: 权限的 ...
- React项目-点餐后台管理系统-react框架实现后台管理系统(包含权限处理)--新手入坑必看!(一)
点餐后台管理系统(react) 一.前言 二.项目介绍 三.相关技术 四.项目实现的功能 4.1.功能分析 4.2.项目结构 4.3.axios封装及mock数据 4.3.1.axios封装 4.3. ...
- 后台管理系统的权限以及vue处理权限的思路
一般来说,在(后台)管理系统(最早的企业级的项目和网站的后台管理系统现在大部分人都叫后台管理系统)中才会有权限之说.权限分为功能级权限和数据级权限.这篇文章主要谈论功能级权限. 一.名词解释: 权限的 ...
- (后台管理系统的权限控制与管理)
此文章根据视频教程进行整理前端面试官必问系列-后台系统的权限控制与管理,建议搭配视频教程一起食用效果更佳 在Web 系统中,权限很久以来一直都只是后端程序所控制的. 为什么呢? 因为Web 系统的本质 ...
- 基于asp.net061旅游网旅行社管理系统
本旅游管理信息系统主要以Visual Studio.NET为主要的网络开发工具,以SQLServer为后台的数据库开发工具.采用ASP.NET技术和C#语言SQL Server数据库技术来完成该系统. ...
- 前端面试必问(后台管理系统的权限控制与管理)
此文章根据视频教程进行整理前端面试官必问系列-后台系统的权限控制与管理,建议搭配视频教程一起食用效果更佳 在Web 系统中,权限很久以来一直都只是后端程序所控制的. 为什么呢? 因为Web 系统的不质 ...
最新文章
- 解密SSL流量,发现隐藏威胁
- 如何在程序中生成崩溃转储dump文件以及如何分析dump
- 有赞的交易系统架构困局以及破局之道
- rabbitmq生产者基于事务实现发送确认
- 还不会子网划分?看这篇文章还远远不够!
- 电脑c语言怎么调出来的,c语言系统源代码_C语言判断系统版本的代码怎样将值调出来啊...
- Spring 实践 -拾遗
- 安卓案例:使用AChartEngine绘制折线图
- linux常用命令-文件处理命令
- 优雅的封装ajax,含跨域
- OpenCV-连环画效果(海贼王yyds)
- 专利服务器拒收 文件解压异常,电子申请常见问题解答20161024.doc-中国专利电子申请网.doc...
- React.js小书结合官方文档第一部分笔记
- android 贴吧列表,Android仿百度贴吧客户端Loading小球
- 外贸建站教程,WordPress外贸自建站流程,会打字即可学会
- 如何删除计算机guest用户,win7如何删掉Guest账户?win7删除Guest账户方法
- 免费svn服务器,百度云开发者教程
- ARCGIS制作图中图——小图/一幅多图
- IDEA 206个快捷键 动图演示,键盘侠标配
- python智能图片识别系统(图片切割、图片识别、区别标识)