用户名/密码认证

本文详细解读了Spring Security中表单登录流程,包括身份验证、重定向、错误处理及自定义登录页面。同时介绍了HTTP基本和摘要认证的实现机制,以及如何在配置中启用和定制。

表单登录

让我们来看看基于表单的登录在 Spring Security 中是如何工作的。首先,我们看到如何将用户重定向到登录表单。
重定向到登录页面
该图构建了我们的 SecurityFilterChain 图。

  1. 首先,用户向未授权的资源/私有发出未经身份验证的请求。
  2. Spring Security 的 FilterSecurityInterceptor 通过抛出 AccessDeniedException 表示拒绝未经身份验证的请求。
  3. 由于用户没有经过身份验证,ExceptionTranslationFilter 启动 Start Authentication,并使用配置的 AuthenticationEntryPoint 重定向到登录页面。在大多数情况下,AuthenticationEntryPoint 是 LoginUrlAuthenticationEntryPoint 的实例。
  4. 浏览器会请求重定向到的登录页面。
  5. 应用程序中的某些内容必须呈现登录页面。
    提交用户名和密码后,UsernamePasswordAuthenticationFilter 对用户名和密码进行身份验证。UsernamePasswordAuthenticationFilter 扩展了 AbstractAuthenticationProcessingFilter,因此这个图看起来应该非常相似。
    用户名密码认证
    该图构建了我们的 SecurityFilterChain 图:
  6. 当用户提交他们的用户名和密码时,UsernamePasswordAuthenticationFilter 通过从 HttpServletRequest 中提取用户名和密码创建一个 UsernamePasswordAuthenticationToken,这是一种身份验证类型。
  7. 接下来,UsernamePasswordAuthenticationToken 被传递到 AuthenticationManager 以进行身份验证。AuthenticationManager 的详细内容取决于用户信息的存储方式。
  8. 如果身份验证失败,则失败:
    • SecurityContextHolder被清空
    • 调用 RememberMeServices.loginFail。如果没有配置 remember me,这是一个 no-op。
    • 调用 AuthenticationFailureHandler
  9. 如果身份验证成功,则为成功:
    • 会在新登录时通知 SessionAuthenticationStrategy。
    • 在 SecurityContextHolder 上设置 身份验证。
    • 调用 RememberMeServices.loginSuccess。如果没有配置 remember me,这是一个 no-op。
    • ApplicationEventPublisher 发布 InteractiveAuthenticationSuccessEvent
    • 调用 AuthenticationSuccessHandler,通常这是一个 SimpleUrlAuthenticationSuccessHandler ,当我们重定向到登录页面时,它会重定向到 ExceptionTranslationFilter 保存的请求。

默认情况下,Spring Security 表单登录处于启用状态。但是,一旦提供了任何基于Servlet的配置,就必须显式提供基于表单的登录。可以在下面找到最低限度的显式Java配置:

protected void configure(HttpSecurity http) {
	http
		// ...
		.formLogin(withDefaults());
}

在这个配置中,Spring Security 将呈现一个默认的登录页面。大多数生产应用程序都需要自定义登录表单。
下面的配置演示了如何提供自定义登录表单:

protected void configure(HttpSecurity http) throws Exception {
	http
		// ...
		.formLogin(form -> form
			.loginPage("/login")
			.permitAll()
		);
}

当在 springsecurity 配置中指定登录页面时,您负责呈现该页面。下面是一个 Thymeleaf 模板,它生成一个 HTML 登录表单,该表单遵循/login 的登录页面:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
	<head>
		<title>Please Log In</title>
	</head>
	<body>
		<h1>Please Log In</h1>
		<div th:if="${param.error}">
			Invalid username and password.</div>
		<div th:if="${param.logout}">
			You have been logged out.</div>
		<form th:action="@{/login}" method="post">
			<div>
			<input type="text" name="username" placeholder="Username"/>
			</div>
			<div>
			<input type="password" name="password" placeholder="Password"/>
			</div>
			<input type="submit" value="Log in" />
		</form>
	</body>
</html>

关于默认的 HTML 表单有几个关键点:

  • 该表单应执行一个 /login 的 Post 提交
  • 表单应该在一个名为 username 的参数中指定用户名
  • 表单应该在名为 password 的参数中指定密码
  • 如果发现 param.error,则表明用户未能提供有效的用户名/密码
  • 如果找到了 param.logout ,则表明用户已成功注销

许多用户只需要自定义登录页面。但是,如果需要,上面的所有内容都可以通过额外的配置进行定制。

如果您正在使用 SpringMVC,那么您将需要一个控制器来将 GET /login 映射到我们创建的登录模板。一个最小的样例 LoginController 可以看到如下:

@Controller
class LoginController {
	@GetMapping("/login")
	String login() {
		return "login";
	}
}

Basic Authentication

让我们来看看在 Spring Security 中 HTTP 基本身份验证是如何工作的。首先,我们看到 WWW-Authenticate 标头被发送回未经身份验证的客户端。
在这里插入图片描述
该图构建了我们的 SecurityFilterChain 图。

  1. 首先,用户向未授权的资源 /private 发出未经身份验证的请求。
  2. Spring Security 的 FilterSecurityInterceptor 通过抛出 AccessDeniedException 表示拒绝未经身份验证的请求。
  3. 由于用户没有经过身份验证,ExceptionTranslationFilter 启动“启动身份验证”。已配置的 AuthenticationEntryPoint 是 BasicAuthenticationEntryPoint 的一个实例,它发送一个 WWW-Authenticate 标头。RequestCache 通常是一个 NullRequestCache,它不保存请求,因为客户机能够重放它最初请求的请求。

当客户端收到 WWW-Authenticate 标头时,它知道应该使用用户名和密码重试。下面是处理用户名和密码的流程。
用户名密码认证
该图构建了我们的 SecurityFilterChain 图:

  1. 当用户提交他们的用户名和密码时,BasicAuthenticationFilter 通过从 HttpServletRequest 中提取用户名和密码来创建一个 UsernamePasswordAuthenticationToken,这是一种身份验证类型。
  2. 接下来,UsernamePasswordAuthenticationToken 被传递到 AuthenticationManager 以进行身份验证。AuthenticationManager 的详细内容取决于用户信息的存储方式。
  3. 如果身份验证失败,则失败:
    • SecurityContextHolder 被清空
    • 调用 RememberMeServices.loginFail。如果没有配置 rememberme,这是一个 no-op。
    • AuthenticationEntryPoint 被调用来触发再次发送的 WWW-Authenticate。
  4. 如果身份验证成功,则为成功:
    • 在 SecurityContextHolder 上设置身份验证
    • 调用 RememberMeServices.loginSuccess。如果没有配置 remember me,这是一个 no-op。
    • BasicAuthenticationFilter 调用 FilterChain.doFilter (请求,响应)以继续应用程序逻辑的其余部分。

Spring Security 的 HTTP 基本身份验证支持在默认情况下是启用的。但是,一旦提供了任何基于 servlet 的配置,就必须显式地提供 HTTP Basic。
一个最小的,显式的配置可以在下面找到:

protected void configure(HttpSecurity http) {
	http
		// ...
		.httpBasic(withDefaults());
}

Digest Authentication

警告: 您不应该在现代应用程序中使用摘要式身份验证,因为它被认为是不安全的。最明显的问题是必须以明文、加密或 md5格式存储密码。所有这些存储格式都是不安全的。相反,您应该使用单向自适应密码散列(即 bCrypt、 PBKDF2、 SCrypt 等)来存储凭据,而这是摘要身份验证不支持的。

摘要身份验证试图解决基本身份验证的许多弱点,特别是通过确保永远不会跨网络以明文形式发送凭据。许多浏览器支持摘要式身份验证。

管理 HTTP 摘要认证的标准是由 RFC 2617定义的,它更新了 RFC 2069规定的摘要认证标准的早期版本。大多数用户代理实现 RFC 2617。Spring Security 的 Digest Authentication 支持与 RFC 2617规定的“ auth”质量保护(qop)兼容,RFC 2617还为向下兼容提供了 RFC 2069。如果您需要使用未加密的 HTTP (即不使用 TLS/HTTPS)并希望最大限度地提高认证过程的安全性,那么摘要认证被认为是一个更有吸引力的选择。然而,每个人都应该使用 HTTPS。

摘要式认证的核心是“ nonce”。这是服务器生成的值。的 nonce 采用了以下格式:

base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
expirationTime:   The date and time when the nonce expires, expressed in milliseconds
key:              A private key to prevent modification of the nonce token

您需要确保使用 NoOpPasswordEncoder 配置不安全的纯文本密码存储。下面提供一个使用 Java 配置配置摘要式身份验证的例子:

@Autowired
UserDetailsService userDetailsService;

DigestAuthenticationEntryPoint entryPoint() {
	DigestAuthenticationEntryPoint result = new DigestAuthenticationEntryPoint();
	result.setRealmName("My App Relam");
	result.setKey("3028472b-da34-4501-bfd8-a355c42bdf92");
}

DigestAuthenticationFilter digestAuthenticationFilter() {
	DigestAuthenticationFilter result = new DigestAuthenticationFilter();
	result.setUserDetailsService(userDetailsService);
	result.setAuthenticationEntryPoint(entryPoint());
}

protected void configure(HttpSecurity http) throws Exception {
	http
		// ...
		.exceptionHandling(e -> e.authenticationEntryPoint(authenticationEntryPoint()))
		.addFilterBefore(digestFilter());
}

参考 SpringSecurity 官方文档

在使用 `wget` 下载资源时,如果遇到 `HTTP 401 Unauthorized: authentication failed` 错误,表示目标服务器要求身份验证,而请求中未提供有效的凭据或提供的凭据无效。以下是解决该问题的几种方法: ### 使用基本认证方式传递用户名密码 可以在 URL 中直接指定用户名密码,格式如下: ``` http://username:password@domain.com/path/to/resource ``` 将 `username` 替换为实际的用户名,`password` 替换为对应的密码,`domain.com` 替换为目标域名,`/path/to/resource` 替换为具体的资源路径。 例如: ```bash wget http://user1:pass123@example.com/restricted/file.txt ``` 需要注意的是,这种方式虽然方便,但存在安全风险,因为密码会以明文形式暴露在命令行历史记录中[^1]。 ### 使用 `--user` 和 `--password` 参数 `wget` 提供了专门用于指定用户名密码的参数,可以避免在 URL 中直接暴露凭据。示例命令如下: ```bash wget --user=username --password=password http://example.com/restricted/file.txt ``` 这种方法同样存在一定的安全隐患,建议仅在测试环境中使用,或者确保终端的历史记录不会被其他人访问到[^1]。 ### 使用 `.netrc` 文件配置认证信息 为了提高安全性并简化每次调用 `wget` 时的命令长度,可以通过编辑用户的 `.netrc` 文件来存储认证信息。`.netrc` 文件通常位于用户的主目录下(如 `/home/user/.netrc` 或 `%USERPROFILE%\.netrc`)。文件内容应包含以下格式的信息: ``` machine example.com login username password password ``` 创建完 `.netrc` 文件后,在执行 `wget` 命令时无需再显式指定用户名密码: ```bash wget http://example.com/restricted/file.txt ``` 需要注意的是,`.netrc` 文件的权限应该设置得足够严格,以防止其他用户读取。可以通过运行 `chmod 600 ~/.netrc` 来限制只有文件所有者才能读写该文件[^1]。 ### 使用 `--load-cookies` 加载 Cookie 文件 如果目标网站支持基于 Cookie 的认证机制,并且已经通过浏览器或其他工具获取到了有效的 Cookie,可以尝试使用 `--load-cookies` 参数加载 Cookie 文件来进行身份验证。首先需要创建一个包含有效 Cookie 的文本文件,然后在 `wget` 命令中引用它: ```bash wget --load-cookies cookies.txt http://example.com/restricted/file.txt ``` 此方法适用于那些不希望通过命令行传递敏感信息的情况,同时也能够绕过复杂的表单提交流程[^1]。 ### 使用 `--header` 自定义 HTTP 请求头 对于某些特殊场景,可能需要手动构造包含认证信息的 HTTP 请求头。这可以通过 `--header` 参数实现。例如,若服务端接受 Bearer Token 认证,则可添加如下参数: ```bash wget --header='Authorization: Bearer your_token_here' http://example.com/restricted/file.txt ``` 这种方式非常灵活,可以根据具体的服务端要求定制不同的认证方案,但同时也增加了配置复杂性和潜在的安全隐患[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值