/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.authentication;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;
/**
* Processes an authentication form submission. Called
* {@code AuthenticationProcessingFilter} prior to Spring Security 3.0.
* <p>
* Login forms must present two parameters to this filter: a username and password. The
* default parameter names to use are contained in the static fields
* {@link #SPRING_SECURITY_FORM_USERNAME_KEY} and
* {@link #SPRING_SECURITY_FORM_PASSWORD_KEY}. The parameter names can also be changed by
* setting the {@code usernameParameter} and {@code passwordParameter} properties.
* <p>
* This filter by default responds to the URL {@code /login}.
*
* @author Ben Alex
* @author Colin Sampaleanu
* @author Luke Taylor
* @since 3.0
*/
/**
* 当用户登录的时候 当前的过滤器会被调用
*/
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
//表单提交的 用户名
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
// 说明 如果用户想增强当前类的功能可以创建一个当前类的子类 在子类的构造方法中 修改SPRING_SECURITY_FORM_USERNAME_KEY和SPRING_SECURITY_FORM_PASSWORD_KEY
// 并且增加到过滤器链中 使用 addfilterAt(new 子类, 当前类.class)
//表单提交的 密码 必须以SPRING_SECURITY_FORM_PASSWORD_KEY 为主
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
// 规定请求路径 以及请求方式
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login",
"POST");
// 用户名 传递参数解析的Key
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
// 密码 传递参数解析的Key
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
// 哪一种方式的登录请求 - 仅限于POST
private boolean postOnly = true;
// 初始化
public UsernamePasswordAuthenticationFilter() {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
}
/**
* 初始化
* @param authenticationManager 认证管理器
*/
public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
}
/*
当用户调用 /login 时会被调用
此方法为重新父类方法。 用户可以继承父类实现自定义的表单登录方法
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
// 如果不是POST调用的话那么抛出 授权服务 异常
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
// 解析获取username
String username = obtainUsername(request);
// 如果user 不为空的话 放入内存并且返回
// 下次在其他用户调用的时候 直接返回而不是重新生成。 这里可能是考虑的用户ID可能会出现多次调用的时候对内存的优化
username = (username != null) ? username.trim() : "";
// 与上述但是直接返回
String password = obtainPassword(request);
password = (password != null) ? password : "";
/**
* UsernamePasswordAuthenticationToken 是在用户登录时 loadUserByUsername 返回的对象一致。用户可以在当前
* 处理自己的登录逻辑
* 由于此处我这里是 使用的oauth 所以在默认的登录中是未授权的 用户
* 至于为什么这里写的这么详细呢 ? 是因为有些课程中老师的习惯是重写这个接口实现用户的登录
* 有的老师是重写的loadUserByUsername 两者皆可 避免混淆
*/
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
//返回
return this.getAuthenticationManager().authenticate(authRequest);
}
/**
* Enables subclasses to override the composition of the password, such as by
* including additional values and a separator.
* <p>
* This might be used for example if a postcode/zipcode was required in addition to
* the password. A delimiter such as a pipe (|) should be used to separate the
* password and extended value(s). The <code>AuthenticationDao</code> will need to
* generate the expected password in a corresponding manner.
* </p>
* @param request so that request attributes can be retrieved
* @return the password that will be presented in the <code>Authentication</code>
* request token to the <code>AuthenticationManager</code>
*/
// 从http请求获取password
@Nullable
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(this.passwordParameter);
}
/**
* Enables subclasses to override the composition of the username, such as by
* including additional values and a separator.
* @param request so that request attributes can be retrieved
* @return the username that will be presented in the <code>Authentication</code>
* request token to the <code>AuthenticationManager</code>
*/
// 从http请求获取username
@Nullable
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(this.usernameParameter);
}
/**
* Provided so that subclasses may configure what is put into the authentication
* request's details property.
* @param request that an authentication request is being created for
* @param authRequest the authentication request object that should have its details
* set
*/
//设置信息 子类可访问
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
}
/**
* Sets the parameter name which will be used to obtain the username from the login
* request.
* @param usernameParameter the parameter name. Defaults to "username".
*/
// 自定义登录的解析参数
public void setUsernameParameter(String usernameParameter) {
Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
this.usernameParameter = usernameParameter;
}
/**
* Sets the parameter name which will be used to obtain the password from the login
* request..
* @param passwordParameter the parameter name. Defaults to "password".
*/
//设置密码的解析Parame 表单的name属性
public void setPasswordParameter(String passwordParameter) {
Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
this.passwordParameter = passwordParameter;
}
/**
* Defines whether only HTTP POST requests will be allowed by this filter. If set to
* true, and an authentication request is received which is not a POST request, an
* exception will be raised immediately and authentication will not be attempted. The
* <tt>unsuccessfulAuthentication()</tt> method will be called as if handling a failed
* authentication.
* <p>
* Defaults to <tt>true</tt> but may be overridden by subclasses.
*/
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public final String getUsernameParameter() {
return this.usernameParameter;
}
public final String getPasswordParameter() {
return this.passwordParameter;
}
}