@Configuration
public class MySpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
//配置权限校验,如:什么地址 必须认证后才能访问,什么地址可以不用认证就可以访问
//权限校验的配置,是线性的。从开始的配置位置开始校验,成功立刻返回。
//校验匹配失败,继续后续的逻辑校验
/**
* Security的权限管理,是基于匹配路径后,授予权限的顺序规则。先通过表达式匹配当前请求路径地址
* 在请求地址符合匹配逻辑的时候,授予访问权限
* 常用的匹配请求路径的方法,执行顺序按照定义依次顺序执行,任何方法匹配,返回后续逻辑不执行
* antMatchers - 最常用,是基于 /*、/**等匹配符号,定义的表达式
* antMatchers(String...path);任意请求方式,都有效的匹配。参数传入的规则任意一个符合匹配逻辑都生效。
* antMatchers(HttpMethod method, String...path);限定了请求方式的匹配规则
* regexMatchers - 排斥,但是推荐使用,相对效率高。(正则表达式)
* regexMatchers(String...path);
* regexMatchers(HttpMethod method, String...path);
* anyRequest - 相当于 /** 的antMatchers匹配。任意请求地址
*
* 授予权限常用方法:
* permitAll() - 免登录访问,任意用户任意状态都可以访问,描述通用和静态资源。如:首页,css,js,images
* anonymous() - 匿名访问,登录后不可访问。 如:登录页面和登录请求
* authenticated() - 登录后可以访问。描述保护资源:如:个人信息,订单查询等
* denyAll() - 任意用户状态都不可访问
* fullyAuthenticated() - 完整登录才可以访问。 描述敏感资源,如:支付操作
* rememberMe() - 记住我,自动登录或N天免登录.描述非敏感资源,
* 上述的所有方法底层调用的都是access();
* access() - 可以实现任何权限授予逻辑。是基于权限表达式的权限授予方法
* 表达式规则由SpringSecurity定义
* 表达式种类:
* 1、定值表达式 access("permitAll") == permitAll()
* 包含:permitAll、anonymous、authenticated、fullyAuthenticated、denyAll、rememberMe
*/
http.authorizeRequests()
//.antMatchers("/toLogin").access("anonymous") //只能匿名访问
.antMatchers("/toLogin").anonymous() //只能匿名访问
.antMatchers("/failure", "/favicon.ico").permitAll() //toLogin请求地址,可以随便访问
.antMatchers("/**/*.js").permitAll() //授予所有目录下的所有.js文件可访问资源
.regexMatchers(".*[.]css").permitAll() //授予所有目录下的所有.css文件可访问权限
.anyRequest().authenticated(); //任意的请求必须认证后才可以访问
//关闭CSRF安全协议,关闭是为了完整流程的可用
http.csrf().disable();
}
}
基于角色的权限管理
/*
Navicat Premium Data Transfer
Source Server : MySQL
Source Server Type : MySQL
Source Server Version : 80021
Source Host : localhost:3306
Source Schema : spring_security
Target Server Type : MySQL
Target Server Version : 80021
File Encoding : 65001
Date: 15/04/2021 10:09:05
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for tb_resource
-- ----------------------------
DROP TABLE IF EXISTS `tb_resource`;
CREATE TABLE `tb_resource` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`res_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限名称',
`res` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限描述符号, 如:admin:query admin:create',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of tb_resource
-- ----------------------------
INSERT INTO `tb_resource` VALUES (1, '管理员查询权限', 'admin:read');
INSERT INTO `tb_resource` VALUES (2, '管理员写权限', 'admin:write');
INSERT INTO `tb_resource` VALUES (3, '访客查询权限', 'guest:write');
INSERT INTO `tb_resource` VALUES (4, '访客写权限', 'guest:write');
-- ----------------------------
-- Table structure for tb_role
-- ----------------------------
DROP TABLE IF EXISTS `tb_role`;
CREATE TABLE `tb_role` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`role_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of tb_role
-- ----------------------------
INSERT INTO `tb_role` VALUES (1, '管理员');
INSERT INTO `tb_role` VALUES (2, '访客');
-- ----------------------------
-- Table structure for tb_role_resource
-- ----------------------------
DROP TABLE IF EXISTS `tb_role_resource`;
CREATE TABLE `tb_role_resource` (
`role_id` int(0) NOT NULL,
`resource_id` int(0) NOT NULL,
PRIMARY KEY (`role_id`, `resource_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of tb_role_resource
-- ----------------------------
INSERT INTO `tb_role_resource` VALUES (1, 1);
INSERT INTO `tb_role_resource` VALUES (1, 2);
INSERT INTO `tb_role_resource` VALUES (2, 3);
INSERT INTO `tb_role_resource` VALUES (2, 4);
-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO `tb_user` VALUES (1, 'admin', '$2a$10$xAex8LhMiQDug1pBKHfRluWgKZQ4t.KpYk64qa5DS3taPVbRddvtu');
INSERT INTO `tb_user` VALUES (2, 'admin1', '$2a$10$cxrVP6O6F02Xsp8ypuY9mOnA9aCk96ql/f6xp9iV2ULghYTTNXo7W');
INSERT INTO `tb_user` VALUES (3, 'admin2', '$2a$10$WEs3VSfo3EY7kT3Ap6Q/auEjJOri57RYsJQLDZqDrVBrSea4hwFs2');
-- ----------------------------
-- Table structure for tb_user_role
-- ----------------------------
DROP TABLE IF EXISTS `tb_user_role`;
CREATE TABLE `tb_user_role` (
`user_id` int(0) NOT NULL,
`role_id` int(0) NOT NULL,
PRIMARY KEY (`user_id`, `role_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of tb_user_role
-- ----------------------------
INSERT INTO `tb_user_role` VALUES (1, 1);
INSERT INTO `tb_user_role` VALUES (2, 2);
INSERT INTO `tb_user_role` VALUES (3, 1);
SET FOREIGN_KEY_CHECKS = 1;
//只根据用户名查询用户,密码的验证,在内存中处理。
@Select("select * from tb_user where username = #{username}")
User selectUserByUsername(String username);
//根据用户主键查询角色名称 tb_role(role_name)
@Select("select role_name from tb_role where id in (select role_id from tb_user_role where user_id = #{id})")
List<String> selectRolesByUserId(Integer id);
//根据用户主键,查询资源描述, tb_resource(res)
@Select("select res from tb_resource where id in (select resource_id from tb_role_resource where role_id in (select role_id from tb_user_role where user_id = #{id}))")
List<String> selectResourceByUserId(Integer id);
@Component
public class SpringSecurityConfiguration implements UserDetailsService {
@Autowired
private UserService userService;
/**
*
* @param username 用户登录的用户名
* @return
* @throws UsernameNotFoundException 当用户查询失败的时候,抛出的异常
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.login(username);
if (user == null) {
//查询失败,用户名不存在。抛出异常。
throw new UsernameNotFoundException("用户名错误...");
}
/**
* 查询成功,用户存储。需要匹配用户名密码是否正确。
* 匹配密码,是由SpringSecurity内部逻辑自动完成。只需要把查询的用户名正确密码返回即可。
* 返回结果,是UserDetails类型的实现类。可以自定义,可以使用Security框架。
* security框架提供的UserDetails接口实现类型User,构造的时候,需要提供3个参数,或者7个参数。
* 3参数构造:
* 用户身份(用户名)、用户正确密码(数据库中的密码),权限集合(Collection集合)
* AuthorityUtils - 工具类,可以通过字符串,创建权限集合。
*/
//新增:用户登录成功后,查询用户的权限集合,包括角色和权限
//角色名称的集合
List<String> roles = userService.findRolesByUser(user);
//权限描述的集合
List<String> resources = userService.findResourcesByUser(user);
//在SpringSecurity中,对角色的命名的要求有严格的规则。要求角色名的前缀必须是ROLE_.不要在数据库中保存ROLE_角色名
//处理角色名称问题
String [] authorities = new String[roles.size() + resources.size()];
for (int i = 0; i < roles.size(); i++) {
authorities[i] = "ROLE_" + roles.get(i);
}
for (int i = 0; i < resources.size(); i++) {
authorities[roles.size() + i] = resources.get(i);
}
System.out.println("用户:" + user.getUsername() + "的权限集合是:" + Arrays.toString(authorities));
org.springframework.security.core.userdetails.User result
= new org.springframework.security.core.userdetails.User(
username,
user.getPassword(),
AuthorityUtils.createAuthorityList(authorities));
return result;
}
}
http.authorizeRequests()
//.antMatchers("/toLogin").access("anonymous") //只能匿名访问
.antMatchers("/toLogin").anonymous() //只能匿名访问
.antMatchers("/failure", "/favicon.ico").permitAll() //toLogin请求地址,可以随便访问
.antMatchers("/**/*.js").permitAll() //授予所有目录下的所有.js文件可访问资源
.regexMatchers(".*[.]css").permitAll() //授予所有目录下的所有.css文件可访问权限
//基于角色的权限管理
//hasRole("XXX")相当于调用access("hasRole('ROLE_XXX')")
.antMatchers("/admin/read").hasRole("管理员") //请求地址为 /admin/read的请求,必须登录用户拥有'管理员'角色才能访问
//hasAnyRole("x","y", "z")相当于调用access("hasAnyRole('ROLE_x','ROLE_y','ROLE_z')")
.antMatchers("/guest/read").hasAnyRole("管理员", "访客") //必须登录用户拥有'管理员'或'访客'角色才能访问
.antMatchers("/guest/write").hasRole("访客") //必须登录用户拥有'访客'角色才能访问
.anyRequest().authenticated(); //任意的请求必须认证后才可以访问
//关闭CSRF安全协议,关闭是为了完整流程的可用
http.csrf().disable();
}
基于资源的权限管理
http.authorizeRequests()
//.antMatchers("/toLogin").access("anonymous") //只能匿名访问
.antMatchers("/toLogin").anonymous() //只能匿名访问
.antMatchers("/failure", "/favicon.ico").permitAll() //toLogin请求地址,可以随便访问
.antMatchers("/**/*.js").permitAll() //授予所有目录下的所有.js文件可访问资源
.regexMatchers(".*[.]css").permitAll() //授予所有目录下的所有.css文件可访问权限
//IP 规则访问需要完整匹配 localhost -> 0:0:0:0:0:0:0:1 -> ::1 127.0.0.1 -> 127.0.0.1
//限制客户端的时候使用,如:提供给内部指定客户端的服务
//客户端地址可以在请求头中查看,解析是由request.getRemoteIpAddr处理的
//.antMatchers("/ip").access("hasIpAddress('::1')") //使用access处理客户端
.antMatchers("/ip").hasIpAddress("127.0.0.1") //客户端IP符合规则的可以访问
//基于资源的权限管理
//hasAuthority("XXX")相当于access(hasAuthority("XXX"))
.antMatchers("/admin/read").hasAuthority("admin:read") //用于admin:read的权限的用户才可以访问
.antMatchers("/admin/write").access("hasAuthority('admin:write')")
.antMatchers("/guest/read").hasAnyAuthority("guest:read","admin:read") //guest:read和admin:read权限的用户才可以访问
//基于角色的权限管理
//hasRole("XXX")相当于调用access("hasRole('ROLE_XXX')")
//.antMatchers("/admin/read").hasRole("管理员") //请求地址为 /admin/read的请求,必须登录用户拥有'管理员'角色才能访问
//hasAnyRole("x","y", "z")相当于调用access("hasAnyRole('ROLE_x','ROLE_y','ROLE_z')")
//.antMatchers("/guest/read").hasAnyRole("管理员", "访客") //必须登录用户拥有'管理员'或'访客'角色才能访问
//.antMatchers("/guest/write").hasRole("访客") //必须登录用户拥有'访客'角色才能访问
.anyRequest().authenticated(); //任意的请求必须认证后才可以访问
//关闭CSRF安全协议,关闭是为了完整流程的可用
http.csrf().disable();
}