使用Spring Boot + Spring Security实现BASIC认证

简要概述

    • Spring Boot と Spring Security を使って BASIC 認証を実現する

 

    今回の環境: Java 11 + Spring Boot 2.2.1 + Spring Security 5.2.1 + Gradle 6.0.1

基本认证是什么?

基本认证——维基百科

基本认证中,将用户名和密码组合使用冒号(:)连接起来,然后编码为Base64格式进行发送。因此,它具有窃听和篡改容易的缺点,但由于几乎所有的Web服务器和浏览器都支持该功能,所以被广泛使用。

HTTP 认证 – HTTP | MDN
HTTP认证 – HTTP | MDN

因为用户ID和密码以明文(尽管经过base64编码,但base64是可逆编码)的形式通过网络传递,基本身份验证方式并不安全。

授权 – HTTP | MDN

Base64编码既不是加密也不是哈希算法。该方法的安全性与明文传输认证信息相当(Base64是可逆编码)。建议在使用Basic认证时与HTTPS结合使用。

引入Spring Security (build.gradle文件中)

在build.gradle中的dependencies中引入spring-boot-starter-security。

implementation 'org.springframework.boot:spring-boot-starter-security'

我在这次使用Spring Initializr时,按照以下指定创建了build.gradle文件。

    • Project: Gradle Project

 

    • Language: Java

 

    • Spring Boot: 2.2.1

 

    • Packaging: Jar

 

    • Java: 11

 

    Dependencies: Spring Security, Spring Web, Thymeleaf

生成的 build.gradle 文件包含以下内容。

plugins {
  id 'org.springframework.boot' version '2.2.1.RELEASE'
  id 'io.spring.dependency-management' version '1.0.8.RELEASE'
  id 'java'
}

group = 'com.example'
version = '0.0.1'
sourceCompatibility = '11'

repositories {
  mavenCentral()
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-security'
  implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
  implementation 'org.springframework.boot:spring-boot-starter-web'
  testImplementation('org.springframework.boot:spring-boot-starter-test') {
    exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
  }
  testImplementation 'org.springframework.security:spring-security-test'
}

test {
  useJUnitPlatform()
}

实现基本身份验证的代码

要实现BASIC认证,只需两个类即可。
一个是实现WebSecurityConfigurer接口的类,用于定义设置信息。
另一个是实现AuthenticationProvider接口的类,用于处理BASIC认证。

为了将其作为Spring Boot运行,需要控制器类等,但此处不详细说明。

准备一个实现WebSecurityConfigurer接口的实现类

我們需要準備一個類來設定:當訪問哪個URL時需要進行身份驗證,使用哪種身份驗證方法等。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;

/**
 * WebSecurity などの設定をするためのクラス。
 * WebSecurityConfigurer インターフェースの実装クラスであり、
 * 抽象クラスである WebSecurityConfigurerAdapter を継承している。
 */
@Configuration
@EnableWebSecurity // Spring Security を有効化
public class DemoBasicAuthConfiguration extends WebSecurityConfigurerAdapter {

  /**
   * WebSecurity の設定をする。
   * 特定のリクエストを無視する設定など、主に全体的なセキュリティ設定を行う。
   * Spring Boot 起動時に呼び出される。
   *
   * @param web 全体的なセキュリティ設定
   * @throws Exception エラーが発生した場合
   */
  @Override
  public void configure(WebSecurity web) throws Exception {

    System.out.println("DemoBasicAuthConfiguration#configure(WebSecurity)");

    // css や js ファイルに認証を必要としない設定を記述
    web.ignoring().antMatchers("/secret-garden/css/**", "/secret-garden/js/**");
  }

  /**
   * HttpSecurity の設定をする。
   * 主に特定の HTTP リクエストに対するセキュリティ設定を実施する。
   * 通常は super で WebSecurityConfigurerAdapter#configure をコールしてはいけない。
   * デフォルトでは http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic(); で構成されている。
   * Spring Boot 起動時に呼び出される。
   *
   * @param http 特定の HTTP リクエストに対するセキュリティ設定
   * @throws Exception エラーが発生した場合
   */
  @Override
  protected void configure(HttpSecurity http) throws Exception {

    System.out.println("DemoBasicAuthConfiguration#configure(HttpSecurity)");

    // CSRF 対策機能を無効化
    http.csrf().disable();

    // 認証が必要な URL を指定
    http.antMatcher("/secret-garden/**");

    // 指定した URL を対象とした認証を有効化
    http.authorizeRequests().anyRequest().authenticated();

    // BASIC 認証を有効化
    // 認証領域 (authentication realm) の名称も指定
    http.httpBasic().realmName("Your secret area.");

    // BASIC 認証の情報は毎回送信されるためセッション管理は不要
    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
  }

  /**
   * AuthenticationManagerBuilder の設定をする。
   * 認証の実装である AuthenticationProvider を指定する。
   * Spring Boot 起動時に呼び出される。
   *
   * @param auth AuthenticationManager を生成するオブジェクト
   * @throws Exception エラーが発生した場合
   */
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {

    System.out.println("DemoBasicAuthConfiguration#configure(AuthenticationManagerBuilder)");

    // 認証に DemoBasicAuthProvider を使用する
    auth.authenticationProvider(new DemoBasicAuthProvider());
  }
}

准备一个实现AuthenticationProvider接口的类。

创建一个用于处理基本认证的类。

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

/**
 * BASIC 認証の簡易実装クラス。
 */
public class DemoBasicAuthProvider implements AuthenticationProvider {

  /**
   * 認証を実行する。
   *
   * @param authentication 認証リクエスト情報
   * @return クレデンシャル情報を含む認証済みの情報
   * @throws AuthenticationException 認証に失敗した場合
   */
  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {

    System.out.println("DemoBasicAuthProvider#authenticate");
    System.out.println(authentication.getClass().getName());

    // 入力されたユーザー名とパスワードを取得
    String inputName = authentication.getName();
    String inputPass = authentication.getCredentials().toString();

    String name = "john-doe"; // 正しいユーザー名
    String pass = "12345678"; // 正しいパスワード

    // ユーザー名とパスワードが正しいかチェック
    if (inputName.equals(name) && inputPass.equals(pass)) {
      System.out.println("ユーザー名とパスワードが正しい");
      // ユーザー名とパスワードを表現する認証済みオブジェクトを返す
      return new UsernamePasswordAuthenticationToken(inputName, inputPass, authentication.getAuthorities());
    } else {
      System.out.println("ユーザー名やパスワードが正しくない");
      throw new BadCredentialsException("ユーザー名やパスワードが正しくない");
    }
  }

  /**
   * このクラスが引数に指定された認証リクエスト情報をサポートするときは true を返す。
   *
   * @param authentication Authentication 型のクラスオブジェクト
   * @return 引数に指定された認証リクエスト情報をサポートするか
   */
  @Override
  public boolean supports(Class<?> authentication) {

    System.out.println("DemoBasicAuthProvider#supports");
    System.out.println(authentication.getName());

    // UsernamePasswordAuthenticationToken として扱える認証リクエストであれば true を返す
    return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
  }
}

运动示例

如果不发送身份验证信息

当收到401 Unauthorized响应时,通常会在常用的Web浏览器中弹出身份验证对话框以便用户输入用户名和密码。

$ curl -i -H "accept: text/html" http://localhost:8080/secret-garden/
HTTP/1.1 401 
WWW-Authenticate: Basic realm="Your secret area."
(以下略)

如果发送了错误的认证信息

返回401 Unauthorized错误。对于大多数常见的Web浏览器,将显示用于输入用户名和密码的身份验证对话框。

$ curl -i -H "accept: text/html" -u john-doe:aaaabbbb http://localhost:8080/secret-garden/
HTTP/1.1 401 
WWW-Authenticate: Basic realm="Your secret area."
(以下略)

如果发送了正确的认证信息

只要指定的 URL 存在内容,就会返回 200 OK。

$ curl -i -H "accept: text/html" -u john-doe:12345678 http://localhost:8080/secret-garden/
HTTP/1.1 200 
(以下略)

请参考以下的资料。
请参照以下资料。
请查阅以下资料。
请查看下列资料。

春季安全全覆盖

    • Spring Security

 

    • Spring Security Reference

 

    • API Document

 

    Hello Spring Security with Boot

WebSecurityConfigurerAdapter 网络安全配置适配器

    • WebSecurityConfigurerAdapter

 

    • Spring BootでWebセキュリティを設定しよう (1/2):CodeZine(コードジン)

 

    Spring SecurityのBasic認証をちょっぴりカスタマイズする – Qiita

验证提供者

    AuthenticationProvider

HttpBasicConfigurer 的本质上是一个配置类(Configurer),用于在Spring Security中启用HTTP基本认证。它提供了一种简单的方式来配置HTTP基本认证的相关属性和选项,以确保安全性。

    HttpBasicConfigurer

基础身份验证入口点

9.3. 认可 — TERASOLUNA Server Framework for Java (5.x) 开发指南 5.5.1.RELEASE 文档

基本身份验证入口点
执行基本身份验证的错误响应。
具体而言,将HTTP响应代码设置为401(未经授权),并将“WWW-Authenticate”头设置为基本身份验证的响应头,然后进行错误响应(HttpServletResponse#sendError)。

spring-security/BasicAuthenticationEntryPoint.java 在 5.2.1.RELEASE 版本的 spring-projects/spring-security 的 GitHub 仓库中。

public void commence(HttpServletRequest request, HttpServletResponse response,
    AuthenticationException authException) throws IOException {
  response.addHeader("WWW-Authenticate", "Basic realm=\"" + realmName + "\"");
  response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
}

访问拒绝处理程序

    • AccessDeniedHandler

 

    spring-security/AccessDeniedHandlerImpl.java at 5.2.1.RELEASE · spring-projects/spring-security · GitHub
bannerAds