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

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 了,两者的集成文章网络上也很多,大家可以自行集成.因为篇幅原因有不少代码省略了,需要的可以参考项目代码

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

(0)

相关推荐

  • Spring security实现登陆和权限角色控制

     随笔简介 1.spring版本:4.3.2.RELEASE+spring security 版本:4.1.2.RELEASE(其它不做说明) 2.所展示内容全部用注解配置 3.springmvc已经配置好,不作说明 4.会涉及到springmvc,spel,el的东西,不熟悉的同学可以先去看一下这方面内容,特别是springmvc 首先想一下,登陆需要什么,最简单的情况下,用户名,密码,然后比对数据库,如果吻合就跳转到个人页面,否则回到登陆页面,并且提示用户名密码错误.这个过程中应该还带有权限

  • 话说Spring Security权限管理(源码详解)

    最近项目需要用到Spring Security的权限控制,故花了点时间简单的去看了一下其权限控制相关的源码(版本为4.2). AccessDecisionManager spring security是通过AccessDecisionManager进行授权管理的,先来张官方图镇楼. AccessDecisionManager AccessDecisionManager 接口定义了如下方法: //调用AccessDecisionVoter进行投票(关键方法) void decide(Authent

  • java中自定义Spring Security权限控制管理示例(实战篇)

    背景描述 项目中需要做细粒的权限控制,细微至url + httpmethod (满足restful,例如: https://.../xxx/users/1, 某些角色只能查看(HTTP GET), 而无权进行增改删(POST, PUT, DELETE)). 表设计 为避嫌,只列出要用到的关键字段,其余敬请自行脑补. 1.admin_user 管理员用户表, 关键字段( id, role_id ). 2.t_role 角色表, 关键字段( id, privilege_id ). 3.t_privi

  • Spring security实现权限管理示例

    Spring security实现权限管理示例,具体如下: 1.配置文件 1.POM.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.o

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

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作. 权限控制是非常常见的功能,在各种后台管理里权限控制更是重中之重.在Spring Boot中使用 Spring Security 构建权限系统是非常轻松和简单的.下面我们就来快速入门 Spring Security .在开始前我们需要一对

  • 在Spring Boot中从类路径加载文件的示例

    资源加载器 使用Java,您可以使用当前线程的classLoader并尝试加载文件,但是Spring Framework为您提供了更为优雅的解决方案,例如ResourceLoader. 您只需要自动连接ResourceLoader,然后调用getResource(„somePath")方法即可. 在Spring Boot(WAR)中从资源目录/类路径加载文件的示例 在以下示例中,我们从类路径中加载名为GeoLite2-Country.mmdb的文件作为资源,然后将其作为File对象检索. @Se

  • Spring Boot中使用Spring Retry重试框架的操作方法

    目录 Spring Retry 在SpringBoot 中的应用 Maven依赖 注解使用 开启Retry功能 注解@Retryable 注解@Recover 注解@CircuitBreaker RetryTemplate RetryTemplate配置 使用RetryTemplate RetryPolicy BackOffPolicy RetryListener 参考 Spring Retry 在SpringBoot 中的应用 Spring Retry提供了自动重新调用失败的操作的功能.这在错

  • Spring Boot 中使用@KafkaListener并发批量接收消息的完整代码

    kakfa是我们在项目开发中经常使用的消息中间件.由于它的写性能非常高,因此,经常会碰到读取Kafka消息队列时拥堵的情况.遇到这种情况时,有时我们不能直接清理整个topic,因为还有别的服务正在使用该topic.因此只能额外启动一个相同名称的consumer-group来加快消息消费(如果该topic只有一个分区,再启动一个新的消费者,没有作用). 完整的代码在这里,欢迎加星号.fork. 官方文档在https://docs.spring.io/spring-kafka/reference/h

  • Spring Boot中整合Spring Security并自定义验证代码实例

    最终效果 1.实现页面访问权限限制 2.用户角色区分,并按照角色区分页面权限 3.实现在数据库中存储用户信息以及角色信息 4.自定义验证代码 效果如下: 1.免验证页面 2.登陆页面 在用户未登录时,访问任意有权限要求的页面都会自动跳转到登陆页面. 3.需登陆才能查看的页面 用户登陆后,可以正常访问页面资源,同时可以正确显示用户登录名: 4.用户有角色区分,可以指定部分页面只允许有相应用户角色的人使用 4.1.只有ADMIN觉得用户才能查看的页面(权限不足) 4.2.只有ADMIN觉得用户才能查

  • 详解如何在spring boot中使用spring security防止CSRF攻击

    CSRF是什么? CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF.  CSRF可以做什么? 你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求.CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全. CSRF漏洞现状 CSRF这种攻击方式

  • Django的用户模块与权限系统的示例代码

    一 导言 设计一个好的用户系统往往不是那么容易,Django提供的用户系统可以快速实现基本的功能,并可以在此基础上继续扩展以满足我们的需求. 先看看Django的用户系统都提供哪些功能: 提供用户模块(User Model) 权限验证(默认添加已有模块的增加删除修改权限) 用户组与组权限功能 用户鉴权与登录功能 与用户登录验证相关的一些函数与装饰方法 如配置了Django的用户系统,仅需调用Django提供的几个函数,便可实现用户的登录,注销,权限验证等功能.例如以下情景 1.登录 # some

  • Spring Security 在 Spring Boot 中的使用详解【集中式】

    1.1 准备 1.1.1 创建 Spring Boot 项目   创建好一个空的 Spring Boot 项目之后,写一个 controller 验证此时是可以直接访问到该控制器的. 1.1.2 引入 Spring Security   在 Spring Boot 中引入 Spring Security 是相当简单的,可以在用脚手架创建项目的时候勾选,也可以创建完毕后在 pom 文件中加入相关依赖. <dependency> <groupId>org.springframework

  • Spring Boot中优雅的获取yml文件工具类

    如何在spring boot中优雅的获取.yml文件工具类呢 代码如下: package com.common.base.utils.base; import com.common.base.generator.ResourceManager; import org.yaml.snakeyaml.Yaml; import java.io.InputStream; import java.util.HashMap; import java.util.Map; /** * yml文件工具类 */ p

  • 在Spring Boot中加载初始化数据的实现

    在Spring Boot中,Spring Boot会自动搜索映射的Entity,并且创建相应的table,但是有时候我们希望自定义某些内容,这时候我们就需要使用到data.sql和schema.sql. 依赖条件 Spring Boot的依赖我们就不将了,因为本例将会有数据库的操作,我们这里使用H2内存数据库方便测试: <dependency> <groupId>com.h2database</groupId> <artifactId>h2</arti

随机推荐