两个依赖的区别
在整个spring中有两个有关security的依赖可以选择: spring-cloud-starter-security和spring-boot-starter-security,很多时候不知道应该选择哪一个,这里做一个介绍:
⛱️先说结论:spring-cloud-starter-security中包含很多依赖包,其中就有spring-boot-starter-security,所以是包含的关系。如果只是基于spring-boot的单体应用,直接使用spring-boot-starter-security即可,如果是基于spring-cloud开发微服务应用,可以选择另一个
spring-boot-starter-security
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
这个依赖中包含很多组件,其中有一个:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
<version>2.2.4.RELEASE</version>
<scope>compile</scope>
</dependency>
继续深挖,该依赖中包含的组件更多,比如gateway、oauth-client和security等:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.3.2.RELEASE</version>
<scope>compile</scope>
</dependency>
到这里就能明白了,spring-cloud-starter-security最终也是依赖spring-boot-starter-security,同时还包括很多其他的cloud组件。
添加依赖
为了纯粹的体验和分析security源码,这里选择spring-boot-starter-security依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
登录Config和Filter
Security实际上是由大量Filter Chain构成,各种各样的Filter负责不同的逻辑,和登录相关的配置默认使用FormLoginConfig类,拦截处理登录的逻辑默认使用UsernamePasswordAuthenticationFilter:
FormLoginConfig
负责配置UsernamePasswordAuthenticationFilter到Chain中,同时设置默认登录相关的信息,源码:
package org.springframework.security.config.annotation.web.configurers;
// 定义比较复杂,各种泛型,后面单独介绍,暂时不用管
public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {
//眼熟吧,这就是默认的用户名和密码参数设置的地方,如果不配置默认就在这里设置的
public FormLoginConfigurer() {
super(new UsernamePasswordAuthenticationFilter(), null);
usernameParameter("username");
passwordParameter("password");
}
//设置登录页面
@Override
public FormLoginConfigurer<H> loginPage(String loginPage) {
return super.loginPage(loginPage);
}
//修改默认的用户名参数
public FormLoginConfigurer<H> usernameParameter(String usernameParameter) {
getAuthenticationFilter().setUsernameParameter(usernameParameter);
return this;
}
//修改默认的密码参数
public FormLoginConfigurer<H> passwordParameter(String passwordParameter) {
getAuthenticationFilter().setPasswordParameter(passwordParameter);
return this;
}
//登录失败后跳转的地址
public FormLoginConfigurer<H> failureForwardUrl(String forwardUrl) {
failureHandler(new ForwardAuthenticationFailureHandler(forwardUrl));
return this;
}
//登录成功后跳转的地址
public FormLoginConfigurer<H> successForwardUrl(String forwardUrl) {
successHandler(new ForwardAuthenticationSuccessHandler(forwardUrl));
return this;
}
@Override
public void init(H http) throws Exception {
super.init(http);
initDefaultLoginFilter(http);
}
private String getUsernameParameter() {
return getAuthenticationFilter().getUsernameParameter();
}
private String getPasswordParameter() {
return getAuthenticationFilter().getPasswordParameter();
}
private void initDefaultLoginFilter(H http) {
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
.getSharedObject(DefaultLoginPageGeneratingFilter.class);
if (loginPageGeneratingFilter != null && !isCustomLoginPage()) {
loginPageGeneratingFilter.setFormLoginEnabled(true);
loginPageGeneratingFilter.setUsernameParameter(getUsernameParameter());
loginPageGeneratingFilter.setPasswordParameter(getPasswordParameter());
loginPageGeneratingFilter.setLoginPageUrl(getLoginPage());
loginPageGeneratingFilter.setFailureUrl(getFailureUrl());
loginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl());
}
}
}
整个类比较简单,部分介绍直接写在注释了。
主要介绍一下方法initDefaultLoginFilter,方法中实例化一个Filter:DefaultLoginPageGeneratingFilter,当采用默认的登录配置时,登录请求的服务器地址、登录页面等都是这个类生成的,简单看下属性:
第2行(选中行)默认定义的登录地址/login就在这里,在该Filter的init方法中,默认设置请求的登录服务和登录页面地址都是/login:
UsernamePasswordAuthenticationFilter
该拦截器主要负责拦击请求,获取登录用户名和密码,然后构建token,最后去认证。整个类也很简单,主要看下核心方法:
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
// 构建Token,为认证做数据准备
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
逻辑比较简单,不再介绍。
修改默认配置
接着上文的例子,继续完善一下:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.usernameParameter("yy_user")
.passwordParameter("yy_pass")
.loginPage("/yylogin.html")
.loginProcessingUrl("/sys/login");
}
}
解释:
- usernameParameter:登录时用户名的参数,默认username,通过该配置可以修改自定义登录界面传值时的用户参数
- passwordParameter:登录时密码的参数,默认password,通过该配置可以修改自定义登录界面传值时的密码参数
- loginPage:请求被拦截后,跳转的登录页,默认/login.html [get],配置自定义的登录界面地址,GET访问
- loginProcessingUrl:登录请求的服务地址,默认/login.html [post],配置登录请求服务,POST访问
通过上述方式,即可修改默认的配置了,简单吧。