欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Boot中使用 Spring Security 构建权限系统的示例代码

程序员文章站 2024-02-16 20:00:25
spring security是一个能够为基于spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在spring应用上下文中配置的bean...

spring security是一个能够为基于spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在spring应用上下文中配置的bean,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

权限控制是非常常见的功能,在各种后台管理里权限控制更是重中之重.在spring boot中使用 spring security 构建权限系统是非常轻松和简单的.下面我们就来快速入门 spring security .在开始前我们需要一对多关系的用户角色类,一个restful的controller.

参考项目代码地址

- 添加 spring security 依赖

首先我默认大家都已经了解 spring boot 了,在 spring boot 项目中添加依赖是非常简单的.把对应的spring-boot-starter-*** 加到pom.xml文件中就行了

<dependency>
   <groupid>org.springframework.boot</groupid>
   <artifactid>spring-boot-starter-security</artifactid>
</dependency>

- 配置 spring security

简单的使用 spring security 只要配置三个类就完成了,分别是:

userdetails

这个接口中规定了用户的几个必须要有的方法

public interface userdetails extends serializable {

 //返回分配给用户的角色列表
 collection<? extends grantedauthority> getauthorities();
 
 //返回密码
 string getpassword();

 //返回帐号
 string getusername();

 // 账户是否未过期
 boolean isaccountnonexpired();

 // 账户是否未锁定
 boolean isaccountnonlocked();

 // 密码是否未过期
 boolean iscredentialsnonexpired();

 // 账户是否激活
 boolean isenabled();
}

userdetailsservice

这个接口只有一个方法 loaduserbyusername,是提供一种用 用户名 查询用户并返回的方法。

public interface userdetailsservice {
 userdetails loaduserbyusername(string var1) throws usernamenotfoundexception;
}

websecurityconfigureradapter

这个内容很多,就不贴代码了,大家可以自己去看.

我们创建三个类分别继承上述三个接口

此 user 类不是我们的数据库里的用户类,是用来安全服务的.

/**
 * created by yuicon on 2017/5/14.
 */
public class user implements userdetails {

 private final string id;
 //帐号,这里是我数据库里的字段
 private final string account;
 //密码
 private final string password;
 //角色集合
 private final collection<? extends grantedauthority> authorities;

 user(string id, string account, string password, collection<? extends grantedauthority> authorities) {
  this.id = id;
  this.account = account;
  this.password = password;
  this.authorities = authorities;
 }

 //返回分配给用户的角色列表
 @override
 public collection<? extends grantedauthority> getauthorities() {
  return authorities;
 }

 @jsonignore
 public string getid() {
  return id;
 }

 @jsonignore
 @override
 public string getpassword() {
  return password;
 }
 
 //虽然我数据库里的字段是 `account` ,这里还是要写成 `getusername()`,因为是继承的接口
 @override
 public string getusername() {
  return account;
 }
 // 账户是否未过期
 @jsonignore
 @override
 public boolean isaccountnonexpired() {
  return true;
 }
 // 账户是否未锁定
 @jsonignore
 @override
 public boolean isaccountnonlocked() {
  return true;
 }
 // 密码是否未过期
 @jsonignore
 @override
 public boolean iscredentialsnonexpired() {
  return true;
 }
 // 账户是否激活
 @jsonignore
 @override
 public boolean isenabled() {
  return true;
 }
}

继承 userdetailsservice

/**
 * created by yuicon on 2017/5/14.
 */
@service
public class userdetailsserviceimpl implements userdetailsservice {
 
 // jpa
 @autowired
 private userrepository userrepository;

 /**
  * 提供一种从用户名可以查到用户并返回的方法
  * @param account 帐号
  * @return userdetails
  * @throws usernamenotfoundexception
  */
 @override
 public userdetails loaduserbyusername(string account) throws usernamenotfoundexception {
  // 这里是数据库里的用户类
  user user = userrepository.findbyaccount(account);

  if (user == null) {
   throw new usernamenotfoundexception(string.format("没有该用户 '%s'.", account));
  } else {
   //这里返回上面继承了 userdetails 接口的用户类,为了简单我们写个工厂类
   return userfactory.create(user);
  }
 }
}

userdetails 工厂类

/**
 * created by yuicon on 2017/5/14.
 */
final class userfactory {

 private userfactory() {
 }

 static user create(user user) {
  return new user(
    user.getid(),
    user.getaccount(),
    user.getpassword(),
   maptograntedauthorities(user.getroles().stream().map(role::getname).collect(collectors.tolist()))
  );
 }
 
 //将与用户类一对多的角色类的名称集合转换为 grantedauthority 集合
 private static list<grantedauthority> maptograntedauthorities(list<string> authorities) {
  return authorities.stream()
    .map(simplegrantedauthority::new)
    .collect(collectors.tolist());
 }
}

重点, 继承 websecurityconfigureradapter 类

/**
 * created by yuicon on 2017/5/14.
 */
@configuration
@enablewebsecurity
@enableglobalmethodsecurity(prepostenabled = true)
public class websecurityconfig extends websecurityconfigureradapter {

 // spring会自动寻找实现接口的类注入,会找到我们的 userdetailsserviceimpl 类
 @autowired
 private userdetailsservice userdetailsservice;

 @autowired
 public void configureauthentication(authenticationmanagerbuilder authenticationmanagerbuilder) throws exception {
  authenticationmanagerbuilder
    // 设置userdetailsservice
    .userdetailsservice(this.userdetailsservice)
    // 使用bcrypt进行密码的hash
    .passwordencoder(passwordencoder());
 }

 // 装载bcrypt密码编码器
 @bean
 public passwordencoder passwordencoder() {
  return new bcryptpasswordencoder();
 }

 //允许跨域
 @bean
 public webmvcconfigurer corsconfigurer() {
  return new webmvcconfigureradapter() {
   @override
   public void addcorsmappings(corsregistry registry) {
    registry.addmapping("/**").allowedorigins("*")
      .allowedmethods("get", "head", "post","put", "delete", "options")
      .allowcredentials(false).maxage(3600);
   }
  };
 }

 @override
 protected void configure(httpsecurity httpsecurity) throws exception {
  httpsecurity
    // 取消csrf
    .csrf().disable()
    // 基于token,所以不需要session
    .sessionmanagement().sessioncreationpolicy(sessioncreationpolicy.stateless).and()
    .authorizerequests()
    .antmatchers(httpmethod.options, "/**").permitall()
    // 允许对于网站静态资源的无授权访问
    .antmatchers(
      httpmethod.get,
      "/",
      "/*.html",
      "/favicon.ico",
      "/**/*.html",
      "/**/*.css",
      "/**/*.js",
      "/webjars/**",
      "/swagger-resources/**",
      "/*/api-docs"
    ).permitall()
    // 对于获取token的rest api要允许匿名访问
    .antmatchers("/auth/**").permitall()
    // 除上面外的所有请求全部需要鉴权认证
    .anyrequest().authenticated();
  // 禁用缓存
  httpsecurity.headers().cachecontrol();
 }
}

- 控制权限到 controller

使用 @preauthorize("hasrole('admin')") 注解就可以了

/**
 * 在 @preauthorize 中我们可以利用内建的 spel 表达式:比如 'hasrole()' 来决定哪些用户有权访问。
 * 需注意的一点是 hasrole 表达式认为每个角色名字前都有一个前缀 'role_'。所以这里的 'admin' 其实在
 * 数据库中存储的是 'role_admin' 。这个 @preauthorize 可以修饰controller也可修饰controller中的方法。
 **/
@restcontroller
@requestmapping("/users")
@preauthorize("hasrole('user')") //有role_user权限的用户可以访问
public class usercontroller {

 @autowired
 private userrepository repository;

 @preauthorize("hasrole('admin')")//有role_admin权限的用户可以访问
 @requestmapping(method = requestmethod.get)
 public list<user> getusers() {
  return repository.findall();
 }
}

- 结语

spring boot中 spring security 的入门非常简单,很快我们就能有一个满足大部分需求的权限系统了.而配合 spring security 的好搭档就是 jwt 了,两者的集成文章网络上也很多,大家可以自行集成.因为篇幅原因有不少代码省略了,需要的可以参考项目代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。