2- 用户登录表单拦截 UsernamePasswordAuthenticationFilter
/* * 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; } }