使用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