整合security结合数据库资源管理
1.数据库.sql文件
SECURITY
/*
SQLyog Ultimate v12.4.3 (64 bit)
MySQL - 5.7.17-log : Database - security
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`security` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `security`;
/*Table structure for table `menu` */
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`pattern` VARCHAR(128) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/*Data for the table `menu` */
INSERT INTO `menu`(`id`,`pattern`) VALUES
(1,'/db/**'),
(2,'/admin/**'),
(3,'/user/**');
/*Table structure for table `menu_role` */
DROP TABLE IF EXISTS `menu_role`;
CREATE TABLE `menu_role` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`mid` INT(11) DEFAULT NULL,
`rid` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/*Data for the table `menu_role` */
INSERT INTO `menu_role`(`id`,`mid`,`rid`) VALUES
(1,1,1),
(2,2,2),
(3,3,3);
/*Table structure for table `role` */
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) DEFAULT NULL,
`nameZh` VARCHAR(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/*Data for the table `role` */
INSERT INTO `role`(`id`,`name`,`nameZh`) VALUES
(1,'ROLE_dba','数据库管理员'),
(2,'ROLE_admin','系统管理员'),
(3,'ROLE_user','用户');
/*Table structure for table `user` */
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) DEFAULT NULL,
`password` VARCHAR(255) DEFAULT NULL,
`enabled` TINYINT(1) DEFAULT NULL,
`locked` TINYINT(1) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/*Data for the table `user` */
INSERT INTO `user`(`id`,`username`,`password`,`enabled`,`locked`) VALUES
(1,'root','$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq',1,0),
(2,'admin','$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq',1,0),
(3,'sang','$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq',1,0);
/*Table structure for table `user_role` */
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`uid` INT(11) DEFAULT NULL,
`rid` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
/*Data for the table `user_role` */
INSERT INTO `user_role`(`id`,`uid`,`rid`) VALUES
(1,1,1),
(2,1,2),
(3,2,2),
(4,3,3);
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
2.创建springboot项目
1.引入这几个依赖
2.配置pom.xml
在mysql换上自己的版本
添加Alibaba的连接池依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.14</version>
</dependency>
3.配置application.properties数据库的参数
spring.datasource.username=root
spring.datasource.password=1024
spring.datasource.url=jdbc:mysql://localhost:3306/SECURITY
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
3.编写各个类
1.编写bean类,role,user,和menu
User:
//需要实现一个接口
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private boolean enabled;
private boolean locked;
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public String getUsername() {
return username;
}
//账户是否未过期
@Override
public boolean isAccountNonExpired() {
return true;
}
//账户是否未锁定
@Override
public boolean isAccountNonLocked() {
return !locked;
}
//凭证是否未过期
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities=new ArrayList<>();
for (Role role:roles){
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
System.out.println(1222);
this.password = password;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setLocked(boolean locked) {
this.locked = locked;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", enable=" + enabled +
", lock=" + locked +
", roles=" + roles +
'}';
}
}
Role:
public class Role {
private Integer id;
private String name;
private String nameZh;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNameZh() {
return nameZh;
}
public void setNameZh(String nameZh) {
this.nameZh = nameZh;
}
@Override
public String toString() {
return "Role{" +
"id=" + id +
", name='" + name + '\'' +
", nameZh='" + nameZh + '\'' +
'}';
}
}
Menu:
public class Role {
private Integer id;
private String name;
private String nameZh;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNameZh() {
return nameZh;
}
public void setNameZh(String nameZh) {
this.nameZh = nameZh;
}
@Override
public String toString() {
return "Role{" +
"id=" + id +
", name='" + name + '\'' +
", nameZh='" + nameZh + '\'' +
'}';
}
}
2.编写登录的service和mapper
mapper:
注意
启动类加上注解
@MapperScan("top.chenyp.mapper")
public interface UserMapper {
@Select("select * from user where username=#{username}")
User loadUserByUsername(String username);
@Select("select * from role where id in (select rid from user_role where uid=#{id})")
List<Role> getRolesById(Integer id);
}
service:
@Service
public class UserService implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user=userMapper.loadUserByUsername(s);
if (user==null){
throw new UsernameNotFoundException("用户不存在!");
}
user.setRoles(userMapper.getRolesById(user.getId()));
return user;
}
}
3.编写登录验证配置类
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
}
写到这里就实现登录功能了
4.编写资源管理接口
mapper:
public interface MenuMapper {
@Select("select *from menu")
@Results({
@Result(id=true,column = "id",property = "id"),
@Result(column = "pattern",property = "pattern"),
@Result(column = "id",property = "roles",
many = @Many(
select = "top.chenyp.mapper.MenuMapper.getRoleById",
fetchType = FetchType.LAZY
)
)
})
List<Menu> getAllMenu();
@Select("select * from role where id in(select rid from menu_role where mid=#{mid})")
public List<Role> getRoleById(int id);
}
service:
@Service
public class MenuService {
@Autowired
MenuMapper menuMapper;
public List<Menu> getAllMenu(){
return menuMapper.getAllMenu();
}
}
5.编写资源管理配置类
MyFilter:
@Component
public class MyFilter implements FilterInvocationSecurityMetadataSource {
AntPathMatcher pathMatcher=new AntPathMatcher();
@Autowired
MenuService menuService;
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
//获取请求的地址
String requestUrl = ((FilterInvocation) o).getRequestUrl();
List<Menu> allMenu = menuService.getAllMenu();
for (Menu menu : allMenu) {
if (pathMatcher.match(menu.getPattern(),requestUrl)){
List<Role> roles=menu.getRoles();
String[] rolesStr=new String[roles.size()];
for (int i = 0; i < roles.size(); i++) {
rolesStr[i]=roles.get(i).getName();
}
return SecurityConfig.createList(rolesStr);
}
}
//返回当前路径拥有的权限
//没有返回默认的ROLE_login
return SecurityConfig.createList("ROLE_login");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
MyAccessDecisionManager:
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
for (ConfigAttribute attribute : collection) {
if ("ROLE_login".equals(attribute.getAttribute())){
if (authentication instanceof AnonymousAuthenticationToken){
throw new AccessDeniedException("非法请求!");
}else{
return;
}
}
//和当前的角色对比
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equals(attribute.getAttribute())){
return;
}
}
}
throw new AccessDeniedException("非法请求!");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
最终的SecurityConfig:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Autowired
MyFilter myFilter;
@Autowired
MyAccessDecisionManager myAccessDecisionManager;
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(myAccessDecisionManager);
o.setSecurityMetadataSource(myFilter);
return o;
}
})
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
}
6.编写controller接口
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello!";
}
@GetMapping("/admin/hello")
public String admin(){
return "hello admin!";
}
@GetMapping("/db/hello")
public String db(){
return "hello db!";
}
@GetMapping("/user/hello")
public String user(){
return "hello user!";
}
}
3.测试
启动项目
账号:root
密码:123 点击登录
登录成功!
root用户具备admin和db两个权限
可以访问/admin/** 或者 /db/**
不能访问/user/**
上图所示在访问/user/hello显示403错误
即权限不足
表示配置成功!