• 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. 初始化数据库

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

    -- ----------------------------
    -- 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依赖
    <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为例,增加系统角色实体等类,其他请参考系统角色自行添加。
    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;
    
    }
    
    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> {
    
    }
    
    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> {
    
    }
    
    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 {
    }
    
    • 增加用户详情实体,用于系统上下文之间传递用户信息
    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,用于系统登录认证和获取用户角色等信息
        需要添加的基础的用户、角色、权限等方法(如查询用户角色、角色权限等方法),请下载项目源码查看。
    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;
    	}
    
    }
    
    • 增加用户登录验证处理类
    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;
    	}
    
    }
    
    • 增加用户权限注解处理类
    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;
    	}
    
    }
    
    • 增加无权限处理类
    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);
    	}
    
    }
    
    • 增加登录失败处理类
    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()));
    	}
    }
    
    • 增加登录成功处理类
    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("登录成功"));
    	}
    }
    
    • 增加登出成功处理类
    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");
    	}
    }
    
    • 增加未登录处理类
    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进行管理。
    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为例)
    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

  • 相关阅读:
    asp.net操作cookie
    前端实现浏览器端大文件分片上传
    百度WebUploader实现浏览器端大文件分片上传
    网页实现浏览器端大文件分片上传
    c#.net实现浏览器端大文件分片上传
    SpringCloud实现浏览器端大文件分片上传
    jsp实现浏览器端大文件分片上传
    Java实现浏览器端大文件分片上传解决方案
    Java实现浏览器端大文件分片上传功能
    Java实现浏览器端大文件分片上传方案
  • 原文地址:https://www.cnblogs.com/cao-lei/p/13896938.html
Copyright © 2020-2023  润新知