一:依赖坐标
org.springframework.boot
spring-boot-starter-security
导入SpringSecurity坐标,启动项目便会出现权限控制的登录界面,默认用户名为user,默认密码会在控制台打印,输入账号密码即可实现登录访问;
如需修改登录的账号密码只需要在配置文件做如下配置即可实现:
spring.security.user.name=fanaozhe
spring.security.user.password=123
二:数据库
实现一个简单的动态权限控制,总共涉及5张表,分别为user(用户)、role(角色)、user_role(用户角色关系)、menu(资源)、menu_role(资源角色);
user表:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for ruser
-- ----------------------------
DROP TABLE IF EXISTS `ruser`;
CREATE TABLE `ruser` (
`id` int(11) NOT NULL,
`username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`enabled` tinyint(1) NULL DEFAULT NULL COMMENT '当前账号是否可用',
`locked` tinyint(1) NULL DEFAULT NULL COMMENT '当前账号是否锁定',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of ruser
-- ----------------------------
INSERT INTO `ruser` VALUES (1, 'root', '123', 1, 0);
INSERT INTO `ruser` VALUES (2, 'admin', '123', 1, 0);
INSERT INTO `ruser` VALUES (3, 'fanaozhe', '123', 1, 0);
SET FOREIGN_KEY_CHECKS = 1;
role表:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL,
`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`nameZh` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'ROLE_dba', '数据库管理员');
INSERT INTO `role` VALUES (2, 'ROLE_admin', '系统管理员');
INSERT INTO `role` VALUES (3, 'ROLE_user', '用户');
SET FOREIGN_KEY_CHECKS = 1;
user_role表:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for ruser_role
-- ----------------------------
DROP TABLE IF EXISTS `ruser_role`;
CREATE TABLE `ruser_role` (
`id` int(11) NOT NULL,
`uid` int(11) NULL DEFAULT NULL,
`rid` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of ruser_role
-- ----------------------------
INSERT INTO `ruser_role` VALUES (1, 1, 1);
INSERT INTO `ruser_role` VALUES (2, 1, 2);
INSERT INTO `ruser_role` VALUES (3, 2, 2);
INSERT INTO `ruser_role` VALUES (4, 3, 3);
SET FOREIGN_KEY_CHECKS = 1;
menu表:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for menu
-- ----------------------------
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu` (
`id` int(11) NOT NULL,
`pattern` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of menu
-- ----------------------------
INSERT INTO `menu` VALUES (1, '/student/**');
INSERT INTO `menu` VALUES (2, '/book/**');
INSERT INTO `menu` VALUES (3, '/user/**');
SET FOREIGN_KEY_CHECKS = 1;
menu_role表:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for menu_role
-- ----------------------------
DROP TABLE IF EXISTS `menu_role`;
CREATE TABLE `menu_role` (
`id` int(11) NOT NULL,
`mid` int(11) NULL DEFAULT NULL,
`rid` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of menu_role
-- ----------------------------
INSERT INTO `menu_role` VALUES (1, 1, 1);
INSERT INTO `menu_role` VALUES (2, 2, 2);
INSERT INTO `menu_role` VALUES (3, 3, 3);
SET FOREIGN_KEY_CHECKS = 1;
三:配置文件
WebSecurityConfig :
package top.fanaozhe.springbootdemo.config.securityconfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import top.fanaozhe.springbootdemo.service.RuserService;
/**
* @author faz
* @create 2020-10-24-14:32
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
RuserService ruserService;
/* @Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}*/
@Bean
PasswordEncoder PasswordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
//设置角色的继承关系
@Bean
RoleHierarchy roleHierarchy(){
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
String hierarchy ="ROLE_dba > ROLE_admin > ROLE_user";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(ruserService);
}
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor() {
@Override
public O postProcess(O object) {
object.setSecurityMetadataSource(cfisms());
object.setAccessDecisionManager(cadm());
return object;
}
})
//登录页面
.and()
.formLogin()
.loginProcessingUrl("/login").permitAll()
.and()
.csrf().disable();
}
@Bean
CustomFilterInvocationSecurityMetadataSource cfisms(){
return new CustomFilterInvocationSecurityMetadataSource();
}
CustomAccessDecisionManager cadm(){
return new CustomAccessDecisionManager();
}
/*//基于内存的配置;
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.antMatchers("/book/**").hasRole("admin")
.antMatchers("/student/**").hasRole("dba")
.antMatchers("/user/**").hasRole("user")
//其他用户访问必须经过认证;
.anyRequest()
.authenticated()
//登录页面
.and()
.formLogin()
.loginProcessingUrl("/login").permitAll()
.and()
.csrf().disable();
}*/
}
CustomFilterInvocationSecurityMetadataSource:
package top.fanaozhe.springbootdemo.config.securityconfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import top.fanaozhe.springbootdemo.entity.Menu;
import top.fanaozhe.springbootdemo.entity.Role;
import top.fanaozhe.springbootdemo.mapper.MenuMapper;
import java.util.Collection;
import java.util.List;
/**
* @author faz
* @create 2020-10-26-19:35
*/
@Component
public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Autowired
MenuMapper menuMapper;
@Override
public Collection getAttributes(Object object) throws IllegalArgumentException {
String requestUrl = ((FilterInvocation) object).getRequestUrl();
List
CustomAccessDecisionManager :
package top.fanaozhe.springbootdemo.config.securityconfig;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
/**
* @author faz
* @create 2020-10-26-20:02
*/
public class CustomAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication auth, Object object,
Collection ca) throws AccessDeniedException, InsufficientAuthenticationException {
Collection extends GrantedAuthority> auths = auth.getAuthorities();
for (ConfigAttribute configAttribute:ca){
if("ROLE_LOGIN".equals(configAttribute.getAttribute())&& auth instanceof UsernamePasswordAuthenticationToken){
return;
}
for(GrantedAuthority authority:auths){
if(configAttribute.getAttribute().equals(authority.getAuthority())){
return;
}
}
}
throw new AccessDeniedException("权限不足");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class> aClass) {
return true;
}
}
四:代码实现
@Mapper
public interface RuserMapper {
/*通过用户名查找用户*/
Ruser findUserByUsername(String username);
/*通过用户id查找用户角色*/
List getUserRolesByUid(Integer id);
}
@Mapper
public interface MenuMapper {
List
五:调试
通过以上代码即可实现一个简单的动态权限控制,实现不同路径资源访问;