Spring Boot构建系统安全层的步骤

01 | Spring Security 架构及核心类

Spring Security 中的过滤器链

Spring Security 中采用的是管道-过滤器(Pipe-Filter)架构模式,这些过滤器链,构成了 Spring Security 的核心。如下图所示:

项目一旦启动,过滤器链将会实现自动配置,如下图所示:

UsernamePasswordAuthenticationFilter 用来检查输入的用户名和密码,代码如下:

public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response)
        throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                "Authentication method not supported: " + request.getMethod());
        }

        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();

        UsernamePasswordAuthenticationToken authRequest =
            new UsernamePasswordAuthenticationToken(username, password);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }
    …
}

BasicAuthenticationFilter 用来认证用户的身份。

FilterSecurityInterceptor 用来判定该请求是否能够访问目标 HTTP 端点。

Spring Security 中的核心类

SecurityContextHolder 存储了应用的安全上下文对象 SecurityContext,包含系统请求中最近使用的认证信息。

一个 HTTP 请求到达系统后,将通过一系列的 Filter 完成用户认证,然后具体的工作交由 AuthenticationManager 完成,AuthenticationManager 成功验证后会返回填充好的 Authentication 实例。

AuthenticationManager 是一个接口,其实现类 ProviderManager 会进一步依赖 AuthenticationProvider 接口完成具体的认证工作。

在 Spring Security 中存在一大批 AuthenticationProvider 接口的实现类,分别完成各种认证操作。在执行具体的认证工作时,Spring Security 势必会使用用户详细信息,UserDetailsService 服务就是用来对用户详细信息实现管理。

02 | 基于 Spring Security 构建用户认证体系

在 Spring Boot 中整合 Spring Security 框架首先需要引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

只要我们在代码工程中添加了上述依赖,包含在该工程中的所有 HTTP 端点都将被保护起来。

在引入 spring-boot-starter-security 依赖之后,Spring Security 会默认创建一个用户名为“user”的账号。当我们访问 AccountController 的 “accounts/{accountId}” 端点时,弹出如下界面:

同时,控制台日志打印如下:

Using generated security password: 17bbf7c4-456a-48f5-a12e-a680066c8f80

因此,访问该接口需要设置如下信息:

每次启动应用时,通过 Spring Security 自动生成的密码都会有所变化。如果我们想设置登录账号和密码,可以在 application.yml 中配置如下:

spring:
  security:
    user:
      name: springcss
      password: springcss_password

配置 Spring Security

初始化用户信息所依赖的配置类是 WebSecurityConfigurer 接口,在日常开发中,我们往往是使用 WebSecurityConfigurerAdapter 类并覆写其中的 configure(AuthenticationManagerBuilder auth) 的方法完成配置工作。

使用 AuthenticationManagerBuilder 类创建一个 AuthenticationManager 就能够轻松实现基于内存、LADP 和 JDBC 的验证。初始化所使用的用户信息只需要指定用户名(Username)、密码(Password)和角色(Role)这三项数据即可。

使用基于内存的用户信息存储方案

@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
    builder.inMemoryAuthentication()
        .withUser("springcss_user")
        .password("password1")
        // 或者使用.authorities("ROLE_USER")
        .roles("USER")
        .and()
        .withUser("springcss_admin")
        .password("password2")
        .roles("USER", "ADMIN");
}

使用基于数据库的用户信息存储方案

表结构如下:

create table users(
  username varchar_ignorecase(50) not null primary key,
  password varchar_ignorecase(500) not null,
  enabled boolean not null
);

create table authorities (
  username varchar_ignorecase(50) not null,
  authority varchar_ignorecase(50) not null,
  constraint fk_authorities_users foreign key(username) references users(username)
);

create unique index ix_auth_username on authorities (username,authority);

Spring Security 的配置代码如下:

@Autowired
DataSource dataSource;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.jdbcAuthentication()
        .dataSource(dataSource)
        .usersByUsernameQuery("select username, password, enabled from Users where username=?")
        .authoritiesByUsernameQuery("select username, authority from UserAuthorities where username=?")
        .passwordEncoder(new BCryptPasswordEncoder());
}

实现定制化用户认证方案

扩展 UserDetails

public class SpringCssUser implements UserDetails {

    private static final long serialVersionUID = 1L;
    private Long id;
    private final String username;
    private final String password;
    private final String phoneNumber;
    // 省略getter/setter
    // 省略重写方法
}

扩展 UserDetailsService

@Service
public class SpringCssUserDetailsService implements UserDetailsService {

    @Autowired
    private SpringCssUserRepository repository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SpringCssUser user = repository.findByUsername(username);
        if (user != null) {
            return user;
        }
        throw new UsernameNotFoundException("SpringCSS User '" + username + "' not found");
    }
}

整合定制化配置

@Configuration
public class SpringCssSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    SpringCssUserDetailsService springCssUserDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(springCssUserDetailsService);
    }
}

03 | 基于 Spring Security 实现安全访问

在日常开发过程中,我们需要对 Web 应用中的不同 HTTP 端点进行不同粒度的权限控制。

对 HTTP 端点进行访问授权管理

使用配置方法

配置方法也是位于 WebSecurityConfigurerAdapter 类中,但使用的是 configure(HttpSecurity http) 方法,如下代码所示:

protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        // 所有请求都需要认证
        .anyRequest()
        // 允许认证用户访问
        .authenticated()
        .and()
        // 需要使用表单进行登录
        .formLogin()
        .and()
        // 使用 HTTP Basic Authentication 方法完成认证
        .httpBasic();
}

Spring Security 还提供了一个 access() 方法,允许开发人员传入一个表达式进行更细粒度的权限控制,这里,我们将引入Spring 框架提供的一种动态表达式语言—— SpEL(Spring Expression Language 的简称)。

只要 SpEL 表达式的返回值为 true,access() 方法就允许用户访问,如下代码所示:

@Override
public void configure(HttpSecurity http) throws Exception {

    http.authorizeRequests()
        .antMatchers("/orders")
        .access("hasRole('ROLE_USER')");
}

使用注解
Spring Security 提供了 @PreAuthorize 注解也可以实现类似的效果,使用该注解代码如下所示:

@RestController
@RequestMapping(value="orders")
public class OrderController {

    @PostMapping(value = "/")
    @PreAuthorize("hasRole(ROLE_ADMIN)")
    public void addOrder(@RequestBody Order order) {
        …
    }
}

@PostAuthorize 主要用于请求结束之后检查权限。

实现多维度访问授权方案

使用用户级别保护服务访问
该级别是最基本的资源保护级别,只要是认证用户就可能访问服务内的各种资源。

@Configuration
public class SpringCssSecurityConfig extends WebSecurityConfigurerAdapter {

    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .anyRequest()
            .authenticated();
    }
}

使用用户+角色级别保护服务访问
该级别在认证用户级别的基础上,还要求用户属于某一个或多个特定角色。

@Configuration
public class SpringCssSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
            .antMatchers("/customers/**")
            .hasRole("ADMIN")
            .anyRequest()
            .authenticated();
    }
}

上述代码表示只有"ADMIN"角色的认证用户才能访问以"/customers/"为根地址的所有 URL。

使用用户+角色+操作级别保护服务访问
该级别在认证用户+角色级别的基础上,对某些 HTTP 操作方法做了访问限制。

@Configuration
public class SpringCssSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception{
        http.authorizeRequests()
                .antMatchers(HttpMethod.DELETE, "/customers/**")
                .hasRole("ADMIN")
                .anyRequest()
                .authenticated();
    }
}

上述代码的效果在于对“/customers”端点执行删除操作时,我们需要使用具有“ADMIN”角色的“springcss_admin”用户,否则会出现“access_denied”错误信息。

以上就是Spring Boot构建系统安全层的步骤的详细内容,更多关于Spring Boot构建系统安全层的资料请关注我们其它相关文章!

(0)

相关推荐

  • SpringBoot集成Swagger构建api文档的操作

    最近在做项目的时候,一直用一个叫做API的东西,controller注解我会写,这个东西我也会用,但是我确实不知道这个东西是个什么,有点神奇.关键还坑了我一次,他的注解会影响到代码的运行,不光是起到注解的作用.所以我就研究了一下. Swagger是什么:THE WORLD'S MOST POPULAR API TOOLING 根据官网的介绍: Swagger Inspector:测试API和生成OpenAPI的开发工具.Swagger Inspector的建立是为了解决开发者的三个主要目标. 1

  • IDEA使用Gradle构建SpringBoot项目工程的详细教程

    背景 最近在研究搭建spring源码调试环境时,接触到到gradle项目构建工具.由于之前习惯于maven项目的构建,故通过此文记录相关gradle的项目构建知识. Gradle Gradle是一个构建工具,用于管理项目依赖和构建项目工程.Gradle抛弃了Maven的基于XML的繁琐配置,采用特定语言Groovy的配置,大大简化了构建代码的行数. 项目结构 Plugin Sample pluginManagement { repositories { gradlePluginPortal()

  • SpringBoot+Docker+IDEA 实现一键构建+推送、运行、同镜像多容器启动(推荐)

    场景: 由于公司项目需要,需要使用部署微服务项目,而之前都是使用tomcat部署,操作十分繁琐.最近正好在学习Docker,利用Docker容器做部署尝试 需求: 使用Docker部署服务 支持idea插件,一键操作构建.推送镜像.运行 单个镜像需根据不同端口部署多个容器 日志持久化 必要软件 服务器(我的版本为CentOS:7.9) Docker(需要开通远程访问,正式环境请使用证书加密,我这里测试用无加密) Harbor 镜像仓库(可选) 开发端 Docker Desktop (支持本地运行

  • 基于SpringBoot构建电商秒杀项目代码实例

    一.项目功能概述 电商秒杀需要完成的3个功能: 1.展示一个商品列表页面,我们可以从中看到可秒杀的商品列表 2.点击进入商品详情页,获取该商品的详细信息 3.秒杀时间开始后,点击进入下单确认页面,并支付成功 二.基于SpringBoot进行项目环境搭建 步骤1:创建一个maven工程,使用quickStart骨架. 步骤2:在pom.xml导入SpringBoot相关依赖. <?xml version="1.0" encoding="UTF-8"?> &

  • Spring Boot2如何构建可部署的war包

    默认情况下Spring Boot使用了内嵌的Tomcat服务器,项目最终被打成jar包运行,每个jar包可以被看作一个独立的Web服务器. 传统的Web开发,一般会将Web应用打成一个war包,然后将其部署到Web服务器中运行. Spring Boot也支持传统的部署模式. 开发环境:IntelliJ IDEA 2019.2.2 Spring Boot版本:2.1.8 1.新建一个名称为demo的Spring Boot项目. 2.修改pom.xml文件 下面粗体部分为所加代码,注释掉原来的bui

  • springboot-2.3.x最新版源码阅读环境搭建(基于gradle构建)

    一.前言 跟很多小伙伴聊天,发现一个严重的问题,很多小伙伴横向发展的貌似很不错,很多技术都能说出一二,但是如果在某个技术上深挖一下就不行了,问啥啥不会.就拿springboot来说,很多同学止步于springboot的应用,再往深处就一问三不知了,那么如何破局呢?smart哥认为最好的办法就是直捣黄龙,要把一个技术理解透了,听别人讲一万遍原理,不如自己撕一遍源码. 要阅读源码那就首先得先搭建源码阅读环境,那么本篇文章就来介绍下Spring Boot的源码环境搭建. 鉴于spring团队已经全面抛

  • IDEA Spring Boot 自动化构建+部署的实现

    本文所有操作都基于Alibaba cloud Toolkit进行操作. 1.插件安装 在插件市场中下载安装. 在IntelliJ IDEA顶部菜单栏中选择File>Settings. 在Settings对话框的左侧导航栏中单击Plugins. 在Plugins区域单击Marketplace. 在搜索栏中输入Alibaba Cloud Toolkit. Search Results区域会出现Alibaba Cloud Toolkit,单击Install. 等待下载.安装完成后,单击Restart

  • Spring Boot 2.4 新特性之一键构建Docker镜像的过程详解

    背景 在我们开发过程中为了支持 Docker 容器化,一般使用 Maven 编译打包然后生成镜像,能够大大提供上线效率,同时能够快速动态扩容,快速回滚,着实很方便.docker-maven-plugin 插件就是为了帮助我们在 Maven 工程中,通过简单的配置,自动生成镜像并推送到仓库中. spotify .fabric8 这里主要使用的主要是如下两种插件 spotify .fabric8 , - -配置通过 xml 定义出 Dockerfile 或者挂载外部 Dockerfile 通过调用

  • SpringBoot集成Swagger2构建在线API文档的代码详解

    第一部分:代码集成 pom.xml <!--swagger2配置--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.4.0</version> </dependency> <dependency> <groupId>i

  • Spring Boot 使用 Swagger 构建 RestAPI 接口文档

    源码地址:https://github.com/laolunsi/spring-boot-examples 目前SpringBoot常被用于开发Java Web应用,特别是前后端分离项目.为方便前后端开发人员进行沟通,我们在SpringBoot引入了Swagger. Swagger作用于接口,让接口数据可视化,尤其适用于Restful APi 本节分两部分介绍,第一部分是SpringBoot引入Swagger的两种方式,第二部分是详细介绍在Web接口上应用Swagger的注解. 本篇文章使用Sp

随机推荐