项目中经常会遇到权限的控制的问题,这次在springboot 中集成security 实现权限控制。
1. 创建spring boot项目用的最新的2.0.4.RELEASE版本,在pom.xml中加入security依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.security 自定义
spring security 中提供了专门的配置类WebSecurityConfigurerAdapter,这里需要研究其中的三个方法
/**
* 配置用户签名服务 主要是user-details 机制,
*
* @param auth 签名管理器构造器,用于构建用户具体权限控制
* @throws Exception
*/
protected void configure(AuthenticationManagerBuilder auth) throws Exception;
/**
* 用来构建 Filter 链
*
* @param web
* @throws Exception
*/
public void configure(WebSecurity web) throws Exception;
/**
* 用来配置拦截保护的请求
*
* @param http
* @throws Exception
*/
protected void configure(HttpSecurity http) throws Exception;
通过对这三个方法的重写,完成zidi自定义配置。
3.创建 security 自定义配置类WebSecurityConfig
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
}
}
4.自定义用户服务信息,这里直接使用数据库签名服务(还有一种内存签名服务,不过只适用于测试开发用,这里直接跳过)。
加入数据源配置:
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=000000
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.database=mysql
spring.jpa.show-sql=true
加入依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
创建表(这里借鉴了网上的一些博客内容,有优化空间,但是需要根据具体需求调整)
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT '',
`descritpion` varchar(10) DEFAULT '',
`url` varchar(10) DEFAULT '',
`pid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
/*Data for the table `sys_permission` */
insert into `sys_permission`(`id`,`name`,`descritpion`,`url`,`pid`) values (3,'ADMIN','','',NULL),(4,'USER','','',NULL);
/*Table structure for table `sys_permission_role` */
DROP TABLE IF EXISTS `sys_permission_role`;
CREATE TABLE `sys_permission_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_id` int(11) DEFAULT NULL,
`permission_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
/*Data for the table `sys_permission_role` */
insert into `sys_permission_role`(`id`,`role_id`,`permission_id`) values (1,2,4),(2,1,3);
/*Table structure for table `sys_role` */
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
/*Data for the table `sys_role` */
insert into `sys_role`(`id`,`name`) values (1,'ADMIN'),(2,'USER');
/*Table structure for table `sys_role_user` */
DROP TABLE IF EXISTS `sys_role_user`;
CREATE TABLE `sys_role_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`Sys_user_id` int(11) DEFAULT NULL,
`sys_role_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
/*Data for the table `sys_role_user` */
insert into `sys_role_user`(`id`,`Sys_user_id`,`sys_role_id`) values (1,2,1),(2,3,2);
/*Table structure for table `sys_user` */
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
/*Data for the table `sys_user` */
insert into `sys_user`(`id`,`name`,`password`) values (2,'admin','$2a$10$Yks2LoqzBUHEWjyLCnsdtepI4oCNip9yNdf67y19ewF8geORNAO5m'),(3,'xuweichao','$2a$10$kmFQOKZw8l776qXp00Lq9e2drL5MUSpG9YHnQtQwbVzyUjJQwHNha');
密码是通过密码编辑器生成的。
创建提提对象和Repository
@Entity
@Table(name = "sys_permission")
public class Permission {
@Id
@Column(name="id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
//权限名称
@Column(name="name")
private String name;
//权限描述
@Column(name = "descritpion")
private String descritpion;
//授权链接
@Column(name = "url")
private String url;
//父节点id
@Column(name = "pid")
private int pid;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescritpion() {
return descritpion;
}
public void setDescritpion(String descritpion) {
this.descritpion = descritpion;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
}
@Entity
@Table(name="sys_role")
public class SysRole {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "name")
private String name;
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;
}
}
@Entity
@Table(name = "sys_user")
public class SysUser {
@Id
@Column(name="id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "name")
private String name;
@Column(name = "password")
private String password;
@Transient
private List<SysRole> roles;
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 getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<SysRole> getRoles() {
return roles;
}
public void setRoles(List<SysRole> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "SysUser{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", roles=" + roles +
'}';
}
}
@Repository
public interface PermissionRepository extends CrudRepository<Permission,Integer> {
}
public interface SysRoleRepository extends CrudRepository<SysRole,Integer> {
}
@Repository
public interface SysUserRepository extends CrudRepository<SysUser,Integer> {
@Query("select a from SysUser a where a.name=:name")
public SysUser getUserByName(@Param("name") String name);
}
业务接口及实现
public interface PermissionService {
public List<Permission> findAll();
public List<Permission> findByAdminUserId(int userId);
}
@Service
public class PermissionServiceImpl implements PermissionService {
@Autowired
PermissionRepository permissionRepository ;
@PersistenceContext
private EntityManager entityManager;
@Override
public List<Permission> findAll() {
return null;
}
@Override
public List<Permission> findByAdminUserId(int userId) {
List<Permission> list = new ArrayList<Permission>();
List<Object[]> abcs = entityManager.createNativeQuery("select p.* \n" +
" from Sys_User u\n" +
" LEFT JOIN sys_role_user sru on u.id= sru.Sys_User_id\n" +
" LEFT JOIN Sys_Role r on sru.Sys_Role_id=r.id\n" +
" LEFT JOIN Sys_permission_role spr on spr.role_id=r.id\n" +
" LEFT JOIN Sys_permission p on p.id =spr.permission_id\n" +
" where u.id="+userId).getResultList();
for (Object[] abc : abcs) {
Permission permission = new Permission();
permission.setId(Integer.valueOf(abc[0]+""));
permission.setName(abc[1]+"");
permission.setDescritpion(abc[2]+"");
permission.setUrl(abc[3]+"");
// permission.setPid(Integer.valueOf(abc[4]+""));
list.add(permission);
}
return list;
}
}
5.设置用户权限方式,spring 提供了一个 UserDetailService 接口,通过它可以获取用户信息。
自定义用户服务类
//自定义userdetailservice
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
SysUserRepository sysUserRepository;
@Autowired
PermissionService permissionService;
@Autowired
PasswordEncoder passwordEncoder;
@Transactional
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = sysUserRepository.getUserByName(username);
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
if (sysUser != null) {
System.err.println("sysUser===============" + sysUser);
//获取用户的授权
List<Permission> permissions = permissionService.findByAdminUserId(sysUser.getId());
//声明授权文件
for (Permission permission : permissions) {
if (permission != null && permission.getName() != null) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+permission.getName());
grantedAuthorities.add(grantedAuthority);
}
}
}
System.err.println("grantedAuthorities===============" + grantedAuthorities);
return new User(sysUser.getName(), sysUser.getPassword(), grantedAuthorities);
}
}
重写protected void configure(AuthenticationManagerBuilder auth) throws Exception 方法。
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 配置用户签名服务 主要是user-details 机制,
*
* @param auth 签名管理器构造器,用于构建用户具体权限控制
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
6.限制请求 重写 protected void configure(HttpSecurity http) throws Exception
http.csrf().disable()//禁用了 csrf 功能
.authorizeRequests()//限定签名成功的请求
.antMatchers("/decision/**","/govern/**").hasAnyRole("USER","ADMIN")//对decision和govern 下的接口 需要 USER 或者 ADMIN 权限
.antMatchers("/admin/login").permitAll()///admin/login 不限定
.antMatchers("/admin/**").hasRole("ADMIN")//对admin下的接口 需要ADMIN权限
.antMatchers("/oauth/**").permitAll()//不拦截 oauth 开放的资源
.anyRequest().permitAll()//其他没有限定的请求,允许访问
.and().anonymous()//对于没有配置权限的其他请求允许匿名访问
.and().formLogin()//使用 spring security 默认登录页面
.and().httpBasic();//启用http 基础验证
}
然后创建几个接口,进行测试。
@RestController
@RequestMapping("admin")
public class MainController {
@RequestMapping("/")
public String index(){
return "index" ;
}
@RequestMapping("/detail")
public String hello(){
return "hello" ;
}
@RequestMapping("/login")
public String login() {
return "login";
}
这里就整合完成了。
后续会加入 OAUTH2.0 做安全认证,敬请期待。