一、Test

环境与上一节的环境一致,这一节主要来进行自定义操作

SecurityConfig 中重写这两个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/js/**", "/css/**","/images/**");
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login.html")
.permitAll()
.and()
.csrf().disable()
;
}

自行写一个登录页面:

login.html 中的核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<form action="/login.html" method="post">
<div class="input" style="text-align: center">
<label for="name">用户名</label>
<input type="text" name="username" id="name">
<span class="spin"></span>
</div>
<div class="input" style="text-align: center">
<label for="pass">密码</label>
<input type="password" name="password" id="pass">
<span class="spin"></span>
</div>
<div class="button login" style="text-align: center">
<button type="submit">
<span>登录</span>
<i class="fa fa-check"></i>
</button>
</div>
</form>

主要注意以下几个方面:

form 的请求地址是 /login.html,方法是 post

用户名和密码的属性值分别是 username 和 password

二、Deep Learning

🐢之疑问一:为啥默认的登录页面和登录接口是一样的?自定义页面又是如何覆盖默认页面的呢?

默认登录页面

image-20200928133357601

关于页面的登录的配置都在:FormLoginConfigurer

其中这个类又是继承 AbstractAuthenticationFilterConfigurer 类的

则在 FormLoginConfigurer 初始化的时候,AbstractAuthenticationFilterConfigurer 的构造方法中:

1
2
3
protected AbstractAuthenticationFilterConfigurer() {
setLoginPage("/login");
}

此时如果没有自定义登录页面,就会是 项目地址/login 这个地址

再看一下 FormLoginConfigurer 中的 init 方法:

1
2
3
4
public void init(H http) throws Exception {
super.init(http);
initDefaultLoginFilter(http);
}

这个方法是调用了父类的 init 方法:

1
2
3
4
5
public void init(B http) throws Exception {
updateAuthenticationDefaults();
updateAccessDefaults(http);
registerDefaultAuthenticationEntryPoint(http);
}

在父类的 init 方法中调用了 updateAuthenticationDefaults() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected final void updateAuthenticationDefaults() {
// 未自定义页面,就会和 loginPage 地址一样
if (loginProcessingUrl == null) {
loginProcessingUrl(loginPage);
}
if (failureHandler == null) {
failureUrl(loginPage + "?error");
}

final LogoutConfigurer<B> logoutConfigurer = getBuilder().getConfigurer(
LogoutConfigurer.class);
if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) {
logoutConfigurer.logoutSuccessUrl(loginPage + "?logout");
}
}

此时就能看到:如果我们没有自定义登录接口,那么登录接口的地址就会被设置为和登录页面一致

自定义登录页面

如果自定义了页面地址,首先会到 FormLoginConfigurer

1
2
3
public FormLoginConfigurer<H> loginPage(String loginPage) {
return super.loginPage(loginPage);
}

在这里面调用了父类的 loginPage 方法:

1
2
3
4
5
6
protected T loginPage(String loginPage) {
setLoginPage(loginPage);
updateAuthenticationDefaults();
this.customLoginPage = true;
return getSelf();
}

可以看到,在这里面 set 完 loginPage 后,又双调用了 updateAuthenticationDefaults() 方法,这个方法在 ☝️ 有

如果自定义了登录接口 loginProcessingUrl ,那么就会进入这个方法:

1
2
3
4
5
6
public T loginProcessingUrl(String loginProcessingUrl) {
this.loginProcessingUrl = loginProcessingUrl;
authFilter
.setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl));
return getSelf();
}

🐢之疑问二:在提交的表单中,参数分别为 username 和 password,可不可以自定义呢?

首先还是去 FormLoginConfigurer 类中看看构造方法:

1
2
3
4
5
public FormLoginConfigurer() {
super(new UsernamePasswordAuthenticationFilter(), null);
usernameParameter("username");
passwordParameter("password");
}

这里的 super 根据 👆 的继承关系,就可以知道是 AbstractAuthenticationFilterConfigurer 中的构造:

1
2
3
4
5
6
7
8
9
protected AbstractAuthenticationFilterConfigurer(F authenticationFilter,
String defaultLoginProcessingUrl) {
this();
// 将传进来的参数赋值给这个类的 authFilter
this.authFilter = authenticationFilter;
if (defaultLoginProcessingUrl != null) {
loginProcessingUrl(defaultLoginProcessingUrl);
}
}

注:这里为什么能传参,是由于

UsernamePasswordAuthenticationFilter 继承 AbstractAuthenticationProcessingFilter

且 F extends AbstractAuthenticationProcessingFilter

image-20200928160655402

此时已经有了 UsernamePasswordAuthenticationFilter 的实例了,接着执行 usernameParameterpasswordParameter 方法:

1
2
3
4
5
6
7
8
9
public FormLoginConfigurer<H> usernameParameter(String usernameParameter) {
getAuthenticationFilter().setUsernameParameter(usernameParameter);
return this;
}

public FormLoginConfigurer<H> passwordParameter(String passwordParameter) {
getAuthenticationFilter().setPasswordParameter(passwordParameter);
return this;
}

这里的 getAuthenticationFilter 是父类的方法:

1
2
3
protected final F getAuthenticationFilter() {
return authFilter;
}

到这里就很明确了,就是将刚才的 new UsernamePasswordAuthenticationFilter() 放到这里面分别调用 setUsernameParametersetPasswordParameter 方法:

1
2
3
4
5
6
7
8
9
public void setUsernameParameter(String usernameParameter) {
Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
this.usernameParameter = usernameParameter;
}

public void setPasswordParameter(String passwordParameter) {
Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
this.passwordParameter = passwordParameter;
}

到这里,usernameParameter 和 passwordParameter 就已经被赋好值了

再通过这个类 (UsernamePasswordAuthenticationFilter) 中的这两个方法从登录请求中拿到这两个值:

1
2
3
4
5
6
7
8
9
@Nullable
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(this.passwordParameter);
}

@Nullable
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(this.usernameParameter);
}

那么自定义就很简单了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login.html")
// 只需要设置以下两项就行
.usernameParameter("user")
.passwordParameter("pwd")
.permitAll()
.and()
.csrf().disable()
;
}

🐢之疑问三:登录成功 or 失败后的重定向的地址,注销登录的地址如何自定义?

登录成功 登录失败
defaultSuccessUrl failureForwardUrl
successForwardUrl failureUrl

区别简单来说就是:

defaultSuccessUrl 可能你访问的地址是 http://localhost:8080/security 但是你没登录,所以你就跳转到 http://localhost:8080/login 页面下,如果你登录成功,那么就会回到 /security 页面

successForwardUrl 不管是什么页面,登录成功一律是你设置的页面

登录失败同理

注销登录

看下面的代码

logoutUrl:用来注销的地址,默认为 “/logout”

logoutRequestMatcher:改变注销地址,还有请求方式

logoutSuccessUrl:注销登录成功后,要跳转的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()

// 注销登录设置
.logout()
.logoutUrl("")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout","POST"))
.logoutSuccessUrl("")

// 登录设置
.and()
.formLogin().loginPage("/login.html")
.usernameParameter("user")
.passwordParameter("pwd")

// 下面两个只需要一个
.defaultSuccessUrl("")
.successForwardUrl("")

// 下面两个只需要一个
.failureForwardUrl("")
.failureUrl("")

.permitAll()
.and()
.csrf().disable()
;
}

评论