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進行動態配置修改的啦~~