SpringBoot + SpringSecurity + Quartz + Layui实现系统权限控制和定时任务

1. 简介#

  Spring Security是一个功能强大且易于扩展的安全框架,主要用于为Java程序提供用户认证(Authentication)和用户授权(Authorization)功能。
  本文将之前博客 SpringBoot2.x集成Quartz实现定时任务管理(持久化到数据库) 改为使用Spring Security进行统一的认证和授权。除了将登录使用Spring Security管理之外,还增加了角色管理、权限管理等菜单,并对系统的权限进行更加细颗粒度的管理和鉴权,使得系统更加安全,便于和其他系统单点集成。

2. Spring Security相关博客(推荐)#

  SpringBoot + SpringSecurity + Mybatis-Plus + JWT实现分布式系统认证和授权
  SpringBoot + SpringSecurity + Mybatis-Plus + JWT + Redis 实现分布式系统认证和授权(刷新Token和Token黑名单)

3. 前期回顾(时间升序)#

  SpringBoot + Layui +Mybatis-plus实现简单后台管理系统(内置安全过滤器)
  SpringBoot基于JustAuth实现第三方授权登录
  SpringBoot + Layui + JustAuth +Mybatis-plus实现可第三方登录的简单后台管理系统
  SpringBoot2.x集成Quartz实现定时任务管理(持久化到数据库)

4. 初始化数据库#

  在之前数据库上增加四张表,分别为:

Copy
-- ---------------------------- -- Table structure for t_sys_auth -- ---------------------------- DROP TABLE IF EXISTS `t_sys_auth`; CREATE TABLE `t_sys_auth` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `auth_name` varchar(50) NOT NULL COMMENT '权限名称', `permission` varchar(200) NULL DEFAULT NULL COMMENT '权限标识', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB COMMENT = '系统权限'; -- ---------------------------- -- Table structure for t_sys_role -- ---------------------------- DROP TABLE IF EXISTS `t_sys_role`; CREATE TABLE `t_sys_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `role_name` varchar(50) NULL DEFAULT NULL COMMENT '角色名称', `role_code` varchar(50) NOT NULL COMMENT '角色编码', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB COMMENT = '系统角色'; -- ---------------------------- -- Table structure for t_sys_role_auth -- ---------------------------- DROP TABLE IF EXISTS `t_sys_role_auth`; CREATE TABLE `t_sys_role_auth` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `role_id` bigint(20) NULL DEFAULT NULL COMMENT '角色ID', `auth_id` bigint(20) NULL DEFAULT NULL COMMENT '权限ID', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB COMMENT = '角色权限关系'; -- ---------------------------- -- Table structure for t_sys_user_role -- ---------------------------- DROP TABLE IF EXISTS `t_sys_user_role`; CREATE TABLE `t_sys_user_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `user_id` bigint(20) NULL DEFAULT NULL COMMENT '用户ID', `role_id` bigint(20) NULL DEFAULT NULL COMMENT '角色ID', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB COMMENT = '用户角色关系';

完整的初始化数据库脚本在项目源码的db文件夹

5. 代码迭代#

  • 修改pom.xml,增加Spring Security依赖
Copy
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.c3stones</groupId> <artifactId>spring-security-quartz-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-security-quartz-demo</name> <description>Spring Security Quartz Demo</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.8.RELEASE</version> <relativePath /> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.4.1</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.11.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
  • 增加刚刚添加的四张表对应的实体、Mapper、Service及Service实现类
      以表t_sys_role为例,增加系统角色实体等类,其他请参考系统角色自行添加。
Copy
import java.io.Serializable; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.Data; import lombok.EqualsAndHashCode; /** * 系统角色 * * @author CL * */ @Data @TableName("t_sys_role") @EqualsAndHashCode(callSuper = false) public class Role extends Model<Role> implements Serializable { private static final long serialVersionUID = 1L; /** * 角色ID */ @TableId(type = IdType.AUTO) private Integer id; /** * 角色名称 */ private String roleName; /** * 角色编码 */ private String roleCode; }
Copy
import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.c3stones.sys.entity.Role; /** * 系统角色Mapper * * @author CL * */ @Mapper public interface RoleMapper extends BaseMapper<Role> { }
Copy
import java.util.List; import javax.validation.constraints.NotNull; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.c3stones.sys.entity.Role; import com.c3stones.sys.entity.User; import com.c3stones.sys.entity.UserRole; /** * 系统角色Service * * @author CL * */ public interface RoleService extends IService<Role> { }
Copy
import java.util.List; import java.util.stream.Collectors; import javax.validation.constraints.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thymeleaf.util.ListUtils; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.c3stones.sys.entity.Role; import com.c3stones.sys.entity.User; import com.c3stones.sys.entity.UserRole; import com.c3stones.sys.mapper.RoleMapper; import com.c3stones.sys.mapper.UserMapper; import com.c3stones.sys.mapper.UserRoleMapper; import com.c3stones.sys.service.RoleService; import cn.hutool.core.util.StrUtil; /** * 系统角色Service实现 * * @author CL * */ @Service public class RoleSerivceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService { }
  • 增加用户详情实体,用于系统上下文之间传递用户信息
Copy
import java.io.Serializable; import java.util.Collection; import org.springframework.security.core.GrantedAuthority; import com.c3stones.sys.entity.User; import lombok.Data; import lombok.EqualsAndHashCode; /** * 系统用户详情 * * @author CL * */ @Data @EqualsAndHashCode(callSuper = false) public class UserDetails extends User implements org.springframework.security.core.userdetails.UserDetails, Serializable { private static final long serialVersionUID = 1L; /** * 用户角色 */ private Collection<GrantedAuthority> authorities; /** * 账号是否过期 */ private boolean isAccountNonExpired = false; /** * 账号是否锁定 */ private boolean isAccountNonLocked = false; /** * 证书是否过期 */ private boolean isCredentialsNonExpired = false; /** * 账号是否有效 */ private boolean isEnabled = true; /** * 获得用户权限 */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } /** * 判断账号是否过期 */ @Override public boolean isAccountNonExpired() { return isAccountNonExpired; } /** * 判断账号是否锁定 */ @Override public boolean isAccountNonLocked() { return isAccountNonLocked; } /** * 判断证书是否过期 */ @Override public boolean isCredentialsNonExpired() { return isCredentialsNonExpired; } /** * 判断账号是否有效 */ @Override public boolean isEnabled() { return isEnabled; } }
  • 增加用户详情Service,用于系统登录认证和获取用户角色等信息
      需要添加的基础的用户、角色、权限等方法(如查询用户角色、角色权限等方法),请下载项目源码查看。
Copy
import java.util.HashSet; import java.util.List; import java.util.Set; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.c3stones.security.entity.UserDetails; import com.c3stones.sys.entity.Role; import com.c3stones.sys.entity.User; import com.c3stones.sys.service.RoleService; import com.c3stones.sys.service.UserService; /** * 用户登录Service * * @author CL * */ @Service public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService { @Autowired private UserService userService; @Autowired private RoleService roleService; /** * 根据用户名查用户信息 * * @param username 用户名称 * @return 用户详细信息 */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("username", username); User user = userService.getOne(queryWrapper); if (user != null) { UserDetails userDetails = new UserDetails(); BeanUtils.copyProperties(user, userDetails); // 用户角色 Set<GrantedAuthority> authorities = new HashSet<>(); // 查询用户角色 List<Role> roleList = roleService.findByUserId(userDetails.getId()); roleList.forEach(role -> { authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleCode())); }); userDetails.setAuthorities(authorities); return userDetails; } return null; } }
  • 增加用户登录验证处理类
Copy
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Component; import com.c3stones.security.entity.UserDetails; import com.c3stones.security.service.UserDetailsService; import cn.hutool.core.util.StrUtil; /** * 用户登录验证处理类 * * @author CL * */ @Component public class UserAuthenticationProvider implements AuthenticationProvider { @Autowired private UserDetailsService userDetailsService; /** * 身份验证 */ @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { // 获取用户名 String username = (String) authentication.getPrincipal(); // 获取密码 String password = (String) authentication.getCredentials(); UserDetails userDetails = (UserDetails) userDetailsService.loadUserByUsername(username); if (userDetails == null) { throw new UsernameNotFoundException("用户名不存在"); } if (!StrUtil.equals(username, userDetails.getUsername()) || !new BCryptPasswordEncoder().matches(password, userDetails.getPassword())) { throw new BadCredentialsException("用户名或密码错误"); } return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities()); } /** * 支持指定的身份验证 */ @Override public boolean supports(Class<?> authentication) { return true; } }
  • 增加用户权限注解处理类
Copy
import java.io.Serializable; import java.util.HashSet; import java.util.List; import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; import com.c3stones.security.entity.UserDetails; import com.c3stones.sys.entity.Auth; import com.c3stones.sys.service.AuthService; /** * 用户权限注解处理类 * * @author CL * */ @Component public class UserPermissionEvaluator implements PermissionEvaluator { @Autowired private AuthService authService; /** * 判断是否拥有权限 * * @param authentication 用户身份 * @param targetUrl 目标路径 * @param permission 路径权限 * * @return 是否拥有权限 */ @Override public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) { UserDetails userDetails = (UserDetails) authentication.getPrincipal(); // 用户权限 Set<String> permissions = new HashSet<String>(); // 查询用户权限 List<Auth> authList = authService.findByUserId(userDetails.getId()); authList.forEach(auth -> { permissions.add(auth.getPermission()); }); // 判断是否拥有权限 if (permissions.stream().filter(p -> (permission.toString().startsWith(p))).count() > 0) { return true; } return false; } @Override public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) { return false; } }
  • 增加无权限处理类
Copy
import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; /** * 无权限处理类 * * @author CL * */ @Component public class UserAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException { response.sendError(403); } }
  • 增加登录失败处理类
Copy
import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.stereotype.Component; import com.c3stones.common.vo.Response; /** * 登录失败处理类 * * @author CL * */ @Component public class UserLoginFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException { Response.responseJson(response, Response.error(500, "登录失败", exception.getMessage())); } }
  • 增加登录成功处理类
Copy
import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.stereotype.Component; import com.c3stones.common.vo.Response; /** * 登录成功处理类 * * @author CL * */ @Component public class UserLoginSuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { Response.responseJson(response, Response.success("登录成功")); } }
  • 增加登出成功处理类
Copy
import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.stereotype.Component; /** * 登出成功处理类 * * @author CL * */ @Component public class UserLogoutSuccessHandler implements LogoutSuccessHandler { @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { SecurityContextHolder.clearContext(); response.sendRedirect("login"); } }
  • 增加未登录处理类
Copy
import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; /** * 未登录处理类 * * @author CL * */ @Component public class UserNotLoginHandler implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { response.sendError(403); } }
  • 增加系统安全核心配置,取代之前的系统配置类、登录拦截器和登录相关认证逻辑
      本系统不是前后端分离系统,因此还是通过Session进行管理。
Copy
import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; import com.c3stones.security.UserAuthenticationProvider; import com.c3stones.security.UserPermissionEvaluator; import com.c3stones.security.handle.UserAccessDeniedHandler; import com.c3stones.security.handle.UserLoginFailureHandler; import com.c3stones.security.handle.UserLoginSuccessHandler; import com.c3stones.security.handle.UserLogoutSuccessHandler; import com.c3stones.security.handle.UserNotLoginHandler; import lombok.Setter; /** * 系统安全核心配置 * * @author CL * */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) @ConfigurationProperties(prefix = "security.web") public class WebSecurityConfig extends WebSecurityConfigurerAdapter { /** * 忽略的URL */ @Setter private List<String> excludes; /** * 无权限处理类 */ @Autowired private UserAccessDeniedHandler userAccessDeniedHandler; /** * 用户未登录处理类 */ @Autowired private UserNotLoginHandler userNotLoginHandler; /** * 用户登录成功处理类 */ @Autowired private UserLoginSuccessHandler userLoginSuccessHandler; /** * 用户登录失败处理类 */ @Autowired private UserLoginFailureHandler userLoginFailureHandler; /** * 用户登出成功处理类 */ @Autowired private UserLogoutSuccessHandler userLogoutSuccessHandler; /** * 用户登录验证 */ @Autowired private UserAuthenticationProvider userAuthenticationProvider; /** * 用户权限注解 */ @Autowired private UserPermissionEvaluator userPermissionEvaluator; /** * 加密方式 * * @return */ @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } /** * 注入自定义PermissionEvaluator * * @return */ @Bean public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler() { DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler(); handler.setPermissionEvaluator(userPermissionEvaluator); return handler; } /** * 用户登录验证 */ @Override protected void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(userAuthenticationProvider); } /** * 安全权限配置 */ @Override protected void configure(HttpSecurity http) throws Exception { http.headers().frameOptions().sameOrigin() // 可以相同域名页面的frame中展示 .and().authorizeRequests() // 权限配置 .antMatchers(excludes.toArray(new String[excludes.size()])).permitAll()// 获取白名单(不进行权限验证) .anyRequest().authenticated() // 其他的需要登陆后才能访问 .and().httpBasic().authenticationEntryPoint(userNotLoginHandler) // 配置未登录处理类 .and().formLogin().loginPage("/login").loginProcessingUrl("/login") // 配置登录URL .successHandler(userLoginSuccessHandler) // 配置登录成功处理类 .failureHandler(userLoginFailureHandler) // 配置登录失败处理类 .and().logout().logoutUrl("/logout")// 配置登出地址 .logoutSuccessHandler(userLogoutSuccessHandler) // 配置用户登出处理类 .and().exceptionHandling().accessDeniedHandler(userAccessDeniedHandler)// 配置没有权限处理类 .and().csrf().disable(); // 禁用跨站请求伪造防护 } }
  • 其他角色、权限添加、删除、绑定等功能,请下载项目源码,启动项目查看
  • 给系统Controller添加权限注解(以UserController为例)
Copy
import javax.validation.constraints.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.c3stones.common.vo.Response; import com.c3stones.sys.entity.User; import com.c3stones.sys.service.UserService; import cn.hutool.crypto.digest.BCrypt; /** * 系统用户Controller * * @author CL * */ @Controller @RequestMapping(value = "user") public class UserController { @Autowired private UserService userService; /** * 查询列表 * * @return */ @PreAuthorize(value = "hasPermission('/user/list', 'sys:user:view')") @RequestMapping(value = "list") public String list() { return "pages/sys/userList"; } /** * 查询列表数据 * * @param user 系统用户 * @param current 当前页 * @param size 每页显示条数 * @return */ @PreAuthorize(value = "hasPermission('/user/listData', 'sys:user:view')") @RequestMapping(value = "listData") @ResponseBody public Response<Page<User>> listData(User user, @RequestParam(name = "page") long current, @RequestParam(name = "limit") long size) { Page<User> page = userService.listData(user, current, size); return Response.success(page); } /** * 新增 * * @return */ @PreAuthorize(value = "hasPermission('/user/add', 'sys:user:edit')") @RequestMapping(value = "add") public String add() { return "pages/sys/userAdd"; } /** * 检验用户名称是否唯一 * * @param userName 用户名称 * @return */ @PreAuthorize(value = "hasPermission('/user/check', 'sys:user:edit')") @RequestMapping(value = "check") @ResponseBody public Response<Boolean> checkUserName(@NotNull String username) { Boolean checkResult = userService.checkUserName(username); return Response.success(checkResult); } /** * 保存 * * @param user 系统用户 * @return */ @PreAuthorize(value = "hasPermission('/user/save', 'sys:user:edit')") @RequestMapping(value = "save") @ResponseBody public Response<Boolean> save(User user) { user.setPassword(BCrypt.hashpw(user.getPassword())); boolean result = userService.save(user); return Response.success(result); } /** * 修改 * * @param user 系统用户 * @param model * @return */ @PreAuthorize(value = "hasPermission('/user/edit', 'sys:user:edit')") @RequestMapping(value = "edit") public String edit(User user, Model model) { Assert.notNull(user.getId(), "ID不能为空"); model.addAttribute("user", userService.getById(user.getId())); return "pages/sys/userEdit"; } /** * 更新 * * @param user 系统用户 * @return */ @PreAuthorize(value = "hasPermission('/user/update', 'sys:user:edit')") @RequestMapping(value = "update") @ResponseBody public Response<Boolean> update(User user) { Assert.notNull(user.getId(), "ID不能为空"); boolean result = userService.updateById(user); return Response.success(result); } /** * 删除 * * @param user 系统用户 * @return */ @PreAuthorize(value = "hasPermission('/user/delete', 'sys:user:edit')") @RequestMapping(value = "delete") @ResponseBody public Response<Boolean> delete(User user) { Assert.notNull(user.getId(), "ID不能为空"); boolean result = userService.removeById(user.getId()); return Response.success(result); } }

6. 测试#

  • 登录(system/123456或user/123456)
      用户名或密码错误

      用户名和密码正确
  • 系统管理员登录(system/123456)
  • 用户管理
  • 用户角色
  • 角色绑定用户

  • 角色权限
  • 权限绑定角色

  • 任务调度
  • 普通用户登录(user/123456)
  • 删除用户

  • 给角色添加用户
  • 任务调度

7. 项目地址#

  spring-security-quartz-demo

posted @   C3Stones  阅读(1088)  评论(3编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示
CONTENTS