RBAC权限控制
1、什么是RBAC权限模型rity
2、RBAC权限模型表设计
3、整合Mybatis数据库
4、UserDetailsService
5、动态查询数据库登陆
6、动态权限角色拦截
什么是RBAC权限模型r
基于角色的权限访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注。在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色。角色可依新的需求和系统的合并而赋予新的权限,而权限也可根据需要而从某角色中回收。角色与角色的关系可以建立起来以囊括更广泛的客观情况。
百度百科:https://baike.baidu.com/item/RBAC/1328788?fr=aladdin
微服务系统中,管理平台也是有分布式的,比如会员管理,订单管理,支付管理等
最终通过SSO将公司颞部所有管理进行整合。 比如用户同一登录 www.toov5.com 进行管理
将权限的设置信息 不要写死 通过表去进行动态的配置 动态的维护 整合Mybatis就OK了
通过表的动态配置
参考前面的写死的配置
数据库环境:
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
`id` int(10) NOT NULL,
`permName` varchar(50) DEFAULT NULL,
`permTag` varchar(50) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL COMMENT '请求url',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO `sys_permission` VALUES ('1', '查询订单', 'showOrder', '/showOrder');
INSERT INTO `sys_permission` VALUES ('2', '添加订单', 'addOrder', '/addOrder');
INSERT INTO `sys_permission` VALUES ('3', '修改订单', 'updateOrder', '/updateOrder');
INSERT INTO `sys_permission` VALUES ('4', '删除订单', 'deleteOrder', '/deleteOrder');
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` int(10) NOT NULL,
`roleName` varchar(50) DEFAULT NULL,
`roleDesc` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', 'admin', '管理员');
INSERT INTO `sys_role` VALUES ('2', 'add_user', '添加管理员');
-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission` (
`role_id` int(10) DEFAULT NULL,
`perm_id` int(10) DEFAULT NULL,
KEY `FK_Reference_3` (`role_id`),
KEY `FK_Reference_4` (`perm_id`),
CONSTRAINT `FK_Reference_4` FOREIGN KEY (`perm_id`) REFERENCES `sys_permission` (`id`),
CONSTRAINT `FK_Reference_3` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
INSERT INTO `sys_role_permission` VALUES ('1', '1');
INSERT INTO `sys_role_permission` VALUES ('1', '2');
INSERT INTO `sys_role_permission` VALUES ('1', '3');
INSERT INTO `sys_role_permission` VALUES ('1', '4');
INSERT INTO `sys_role_permission` VALUES ('2', '1');
INSERT INTO `sys_role_permission` VALUES ('2', '2');
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int(10) NOT NULL,
`username` varchar(50) DEFAULT NULL,
`realname` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`createDate` date DEFAULT NULL,
`lastLoginTime` date DEFAULT NULL,
`enabled` int(5) DEFAULT NULL,
`accountNonExpired` int(5) DEFAULT NULL,
`accountNonLocked` int(5) DEFAULT NULL,
`credentialsNonExpired` int(5) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', '张三', '15a013bcac0c50049356b322e955035e\r\n', '2018-11-13', '2018-11-13', '1', '1', '1', '1');
INSERT INTO `sys_user` VALUES ('2', 'userAdd', '小余', '15a013bcac0c50049356b322e955035e\r\n', '2018-11-13', '2018-11-13', '1', '1', '1', '1');
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`user_id` int(10) DEFAULT NULL,
`role_id` int(10) DEFAULT NULL,
KEY `FK_Reference_1` (`user_id`),
KEY `FK_Reference_2` (`role_id`),
CONSTRAINT `FK_Reference_2` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`),
CONSTRAINT `FK_Reference_1` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '1');
INSERT INTO `sys_user_role` VALUES ('2', '2');
对密码需要加密 md5加密
对传入到后台的数据进行比对:
对于加密后的处理逻辑:
maven:
<!-->spring-boot 整合security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- springboot 整合mybatis框架 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!-- alibaba的druid数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
用户登录时候 会去各个表中查询出信息
包括权限信息
通过查询出来的信息给配置文件赋值,动态赋值。
管理者可以通过管理这些表去进行权限的管理。
对于User的bean字段需要严格按照规范去写,框架已经定义了接口,需要去实现:
// 用户信息表 @Data public class User implements UserDetails { //框架地层查询时候 必须依赖的字段 实现这个接口 规范了名称 private Integer id; private String username; private String realname; private String password; private Date createDate; private Date lastLoginTime; private boolean enabled; private boolean accountNonExpired; private boolean accountNonLocked; private boolean credentialsNonExpired; // 用户所有权限 private List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); public Collection<? extends GrantedAuthority> getAuthorities() { //一个用户可能多个权限 所以用了集合去处理存储 return authorities; } }
对于数据库的查询:Mapper层
import java.util.List; import org.apache.ibatis.annotations.Select; import com.mayikt.entity.Permission; public interface PermissionMapper { // 查询苏所有权限 @Select(" select * from sys_permission ") List<Permission> findAllPermission(); }
import java.util.List; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import com.mayikt.entity.Permission; import com.mayikt.entity.User; public interface UserMapper { // 查询用户信息 @Select(" select * from sys_user where username = #{userName}") User findByUsername(@Param("userName") String userName); // 查询用户的权限 @Select(" select permission.* from sys_user user" + " inner join sys_user_role user_role" + " on user.id = user_role.user_id inner join " + "sys_role_permission role_permission on user_role.role_id = role_permission.role_id " + " inner join sys_permission permission on role_permission.perm_id = permission.id where user.username = #{userName};") List<Permission> findPermissionByUsername(@Param("userName") String userName); }
登录时候会首先执行的Java类:
先查询出User信息
使用UserDetailsService实现动态查询数据库验证账号
import java.util.ArrayList; import java.util.List; 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.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import com.mayikt.entity.Permission; import com.mayikt.entity.User; import com.mayikt.mapper.UserMapper; // 设置动态用户信息 @Service public class MyUserDetailsService implements UserDetailsService { @Autowired private UserMapper userMapper; @Override //用户登录时候会调用这个方法 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 1.根据用户名称查询数据用户信息 User user = userMapper.findByUsername(username); // 2.底层会根据数据库查询用户信息,判断密码是否正确 开发者需要做的就是查询就OK了 // 3. 给用户设置权限 查询出来 然后赋值就OK了 List<Permission> listPermission = userMapper.findPermissionByUsername(username); System.out.println("username:" + username + ",对应权限:" + listPermission.toString()); if (listPermission != null && listPermission.size() > 0) { // 定义用户权限 List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); for (Permission permission : listPermission) { authorities.add(new SimpleGrantedAuthority(permission.getPermTag())); } user.setAuthorities(authorities); } return user; } }
进行权限的校验 设置:
import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; 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.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; import com.mayikt.entity.Permission; import com.mayikt.handler.MyAuthenticationFailureHandler; import com.mayikt.handler.MyAuthenticationSuccessHandler; import com.mayikt.mapper.PermissionMapper; import com.mayikt.security.MyUserDetailsService; import com.mayikt.utils.MD5Util; // Security 配置 @Component @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyAuthenticationFailureHandler failureHandler; @Autowired private MyAuthenticationSuccessHandler successHandler; @Autowired private MyUserDetailsService myUserDetailsService; @Autowired private PermissionMapper permissionMapper; // 配置认证用户信息和权限 protected void configure(AuthenticationManagerBuilder auth) throws Exception { // // 添加admin账号 // auth.inMemoryAuthentication().withUser("admin").password("123456"). // authorities("showOrder","addOrder","updateOrder","deleteOrder"); // // 添加userAdd账号 // auth.inMemoryAuthentication().withUser("userAdd").password("123456").authorities("showOrder","addOrder"); // 如果想实现动态账号与数据库关联 在该地方改为查询数据库 auth.userDetailsService(myUserDetailsService).passwordEncoder(new PasswordEncoder() { // 加密的密码与数据库密码进行比对CharSequence rawPassword 表单字段 encodedPassword // 数据库加密字段 public boolean matches(CharSequence rawPassword, String encodedPassword) { System.out.println("rawPassword:" + rawPassword + ",encodedPassword:" + encodedPassword); // 返回true 表示认证成功 返回fasle 认证失败 Boolean reslt = MD5Util.encode((String) rawPassword).equals(encodedPassword); System.out.println("result结果:"+reslt); return reslt; } // 对表单密码进行加密 public String encode(CharSequence rawPassword) { System.out.println("rawPassword:" + rawPassword); return MD5Util.encode((String) rawPassword); } }); } // 配置拦截请求资源 进行动态请求资源 protected void configure(HttpSecurity http) throws Exception { ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests = http .authorizeRequests(); // 1.读取数据库权限列表 List<Permission> listPermission = permissionMapper.findAllPermission(); for (Permission permission : listPermission) { // 设置权限 authorizeRequests.antMatchers(permission.getUrl()).hasAnyAuthority(permission.getPermTag()); } authorizeRequests.antMatchers("/login").permitAll().antMatchers("/**").fullyAuthenticated().and().formLogin() .loginPage("/login").successHandler(successHandler).and().csrf().disable(); } @Bean public static NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } }
yml:
# 配置freemarker spring: freemarker: # 设置模板后缀名 suffix: .ftl # 设置文档类型 content-type: text/html # 设置页面编码格式 charset: UTF-8 # 设置页面缓存 cache: false # 设置ftl文件路径 template-loader-path: - classpath:/templates # 设置静态文件路径,js,css等 mvc: static-path-pattern: /static/** ####整合数据库层 datasource: name: test url: jdbc:mysql://127.0.0.1:3306/rbac_db username: root password: root # druid 连接池 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver
加密工具:
import java.security.MessageDigest; public class MD5Util { // 加盐 private static final String SALT = "toov5"; public static String encode(String password) { password = password + SALT; MessageDigest md5 = null; try { md5 = MessageDigest.getInstance("MD5"); } catch (Exception e) { throw new RuntimeException(e); } char[] charArray = password.toCharArray(); byte[] byteArray = new byte[charArray.length]; for (int i = 0; i < charArray.length; i++) byteArray[i] = (byte) charArray[i]; byte[] md5Bytes = md5.digest(byteArray); StringBuffer hexValue = new StringBuffer(); for (int i = 0; i < md5Bytes.length; i++) { int val = ((int) md5Bytes[i]) & 0xff; if (val < 16) { hexValue.append("0"); } hexValue.append(Integer.toHexString(val)); } return hexValue.toString(); } public static void main(String[] args) { System.out.println(MD5Util.encode("123456")); } }
启动类:
import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @MapperScan("com.toov5.mapper") @SpringBootApplication public class AppSecurity { public static void main(String[] args) { SpringApplication.run(AppSecurity.class, args); // Security 两种模式 fromLogin 表单提交认证模式 httpBasic 浏览器与服务器做认证授权 } }
这样就成为了 根据SQL进行动态配置修改的啦~~