通常系統(tǒng)會(huì)擁有大量的用戶怖竭,同時(shí)系統(tǒng)也會(huì)劃分非常多的權(quán)限恰力,為了能夠更方便的管理用戶和權(quán)限叉谜,往往需要隔離用戶和權(quán)限,使它們不直接建立聯(lián)系踩萎,而是通過“分組”停局,即將這些權(quán)限通過一定的邏輯劃分為若干組,每個(gè)組包含若干權(quán)限,每個(gè)用戶有屬于若干組董栽,如圖所示码倦。
這種將權(quán)限進(jìn)行打包處理的方式可以適應(yīng)一下場景:
- 需要將用戶劃分為不同的組,而組之間的權(quán)限是重疊的锭碳。
- 需要全局地袁稽、批量的修改一類用戶的權(quán)限。
- 當(dāng)擁有大量用戶時(shí)擒抛,避免直接面向用戶級(jí)別授權(quán)推汽。
加入“分組”的方式一定程度增加了用戶權(quán)限管理的復(fù)雜,但其帶來的低耦合歧沪、易維護(hù)的價(jià)值已遠(yuǎn)超過復(fù)雜性歹撒。這種將用戶權(quán)限集中到組的技術(shù)通常稱為基于組的用戶訪問控制,即Group Based Access Control槽畔,GBAC(屬于RBAC的一種)栈妆。下面就為大家講解一下Spring Security是如何實(shí)現(xiàn)GBAC的胁编。
1.根據(jù)上圖厢钧,生成MySql數(shù)據(jù)庫腳本和測試數(shù)據(jù)
數(shù)據(jù)庫中,Authorities表中的數(shù)據(jù)已經(jīng)清空嬉橙,我們之前建立的用戶和權(quán)限的對(duì)應(yīng)關(guān)系已經(jīng)不存在早直。
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for authorities
-- ----------------------------
DROP TABLE IF EXISTS `authorities`;
CREATE TABLE `authorities` (
`username` varchar(50) NOT NULL,
`authority` varchar(50) NOT NULL,
UNIQUE KEY `ix_auth_username` (`username`,`authority`),
CONSTRAINT `fk_authorities_users` FOREIGN KEY (`username`) REFERENCES `users` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of authorities
-- ----------------------------
-- ----------------------------
-- Table structure for groups
-- ----------------------------
DROP TABLE IF EXISTS `groups`;
CREATE TABLE `groups` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`group_name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of groups
-- ----------------------------
INSERT INTO `groups` VALUES ('1', 'Users');
INSERT INTO `groups` VALUES ('2', 'Administrators');
-- ----------------------------
-- Table structure for group_authorities
-- ----------------------------
DROP TABLE IF EXISTS `group_authorities`;
CREATE TABLE `group_authorities` (
`group_id` bigint(20) NOT NULL,
`authority` varchar(50) NOT NULL,
KEY `fk_group_authorities_group` (`group_id`),
CONSTRAINT `fk_group_authorities_group` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of group_authorities
-- ----------------------------
INSERT INTO `group_authorities` VALUES ('1', 'ROLE_USER');
INSERT INTO `group_authorities` VALUES ('2', 'ROLE_USER');
INSERT INTO `group_authorities` VALUES ('2', 'ROLE_ADMIN');
-- ----------------------------
-- Table structure for group_members
-- ----------------------------
DROP TABLE IF EXISTS `group_members`;
CREATE TABLE `group_members` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`group_id` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_group_members_group` (`group_id`),
CONSTRAINT `fk_group_members_group` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of group_members
-- ----------------------------
INSERT INTO `group_members` VALUES ('1', 'user', '1');
INSERT INTO `group_members` VALUES ('2', 'admin', '2');
-- ----------------------------
-- Table structure for persistent_logins
-- ----------------------------
DROP TABLE IF EXISTS `persistent_logins`;
CREATE TABLE `persistent_logins` (
`username` varchar(64) NOT NULL,
`series` varchar(64) NOT NULL,
`token` varchar(64) NOT NULL,
`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of persistent_logins
-- ----------------------------
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`username` varchar(50) NOT NULL,
`password` varchar(500) NOT NULL,
`enabled` tinyint(1) NOT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('admin', '$2a$10$tpJFaP1dBCLWGpPsreD8zOE2HxOJzuZ3cRHmhOODYSZLUrmlFkySi', '1');
INSERT INTO `users` VALUES ('user', '$2a$10$tpJFaP1dBCLWGpPsreD8zOE2HxOJzuZ3cRHmhOODYSZLUrmlFkySi', '1');
SET FOREIGN_KEY_CHECKS=1;
修改WebSecurityConfigurerAdapter開啟Spring Security分組功能,并關(guān)閉用戶直接獲取權(quán)限的功能市框。同時(shí)將Spring Security默認(rèn)創(chuàng)建的UserDetailsService發(fā)布為Spring的bean霞扬,這樣我們就可一直獲取該方法,完成一些基本的用戶管理功能枫振,比如創(chuàng)建喻圃、刪除用戶,加入分組等粪滤。
/**
* Description:配置認(rèn)證細(xì)節(jié)
*
* @param auth
* @return
* @Author: 瓦力
* @Date: 2017/7/21 17:04
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.passwordEncoder(passwordEncoder())//啟用密碼加密功能
.dataSource(dataSource);
}
/**
* 獲取默認(rèn)創(chuàng)建的UserDetailsService斧拍,開啟分組功能,關(guān)閉用戶直接授權(quán)功能杖小,并發(fā)布為Spring Bean
*
* @param auth
* @return
*/
@Bean
@Autowired
public UserDetailsService userDetailsService(AuthenticationManagerBuilder auth) {
UserDetailsService userDetailsService = auth.getDefaultUserDetailsService();
if (JdbcUserDetailsManager.class.isInstance(userDetailsService)) {
JdbcUserDetailsManager jdbcUserDetailsManager = (JdbcUserDetailsManager) userDetailsService;
jdbcUserDetailsManager.setEnableGroups(true);//開啟分組功能
jdbcUserDetailsManager.setEnableAuthorities(false);//關(guān)閉用戶直接獲取權(quán)限功能
}
return userDetailsService;
}
利用UserDetailsService建立用戶管理服務(wù)
@Component
public class UserService {
@Autowired
UserDetailsService userDetailsService;
@PostConstruct
public void init() {
System.out.println("---------------------------");
System.out.println(userDetailsService.getClass());
System.out.println("---------------------------");
}
}
啟動(dòng)服務(wù)器肆汹,驗(yàn)證用戶認(rèn)證。
這樣我們非常簡單的實(shí)現(xiàn)了基于GBAC的認(rèn)證功能
代碼示例:https://github.com/wexgundam/spring.security/tree/master/ch08