学习基于记录,而不止于记录。
希望自己能坚持下去~
0.写在前面
继续上一篇博客的内容,记录自定义登录成功和失败处理逻辑。
spring boot版本:2.2.7.RELEASE
开发工具:IntelliJ IDEA 2018.3.2 (Ultimate Edition)
jdk: java version "1.8.0_181"
maven: 3.3.9
1.自定义登录成功处理的实现
默认是要实现AuthenticationSuccessHandler接口,但是一般不使用这种方式,而是继承SavedRequestAwareAuthenticationSuccessHandler类,这个类继承的父类实际上已经实现了接口,另外子类功能更强大能够满足开发需求。
MyAuthenticationSuccessHandler.java
自定义的登录成功处理类
package com.grj.securityDemo.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.grj.securityDemo.model.MyResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* securit登录成功自定义处理类
* @Author: grj
* @Date: 2020/6/2 20:25
*/
@Component
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
//从application.properties读取值存入
@Value("${spring.security.loginType}")
private String loginType;
private static ObjectMapper mapper = new ObjectMapper();
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
//如果是json类型,则返回json数据,否则继续使用父类方法跳转登陆之前请求的页面
if(loginType.equalsIgnoreCase("json")) {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(mapper.writeValueAsString(MyResponse.getSuccessInstance()));
}else {
super.onAuthenticationSuccess(request,response,authentication);
}
}
}
MyResponse.java
这是我自定义的返回数据实体model,里面使用了Lombok依赖注解
package com.grj.securityDemo.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author: grj
* @Date: 2020/6/2 20:36
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MyResponse {
public enum ResponseTypeEnum {NULL, SUCCESS, ERROR, WARNING};
private Integer code;
private ResponseTypeEnum type;
private String message;
private Object data;
public static MyResponse getSuccessInstance() {
return MyResponse.builder()
.code(200)
.type(ResponseTypeEnum.SUCCESS)
.data(null)
.message("请求成功!")
.build();
}
public static MyResponse getFailureInstance() {
return MyResponse.builder()
.code(500)
.type(ResponseTypeEnum.ERROR)
.data(null)
.message("请求失败!")
.build();
}
}
SecurityConfig.java
package com.grj.securityDemo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @Author: grj
* @Date: 2020/6/1 20:10
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
//关闭这项拦截功能,要不然后面测试都无法进行
http.csrf().disable();
//登录任何人都可以访问
//pageA和pageB只有拥有user或者admin权限才可以访问
//syslog和sysuser只有拥有admin权限才可以访问
http.formLogin()
.loginPage("/login.html")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/login")
.failureForwardUrl("/login.html")
//注意defaultSuccessUrl和successHandler只能存在一种处理方式
// .defaultSuccessUrl("/index")
.successHandler(myAuthenticationSuccessHandler)
.and()
.authorizeRequests()
.antMatchers("/login.html", "/login").permitAll()
.antMatchers("/pageA.html", "pageB.html")
.hasAnyAuthority("ROLE_user", "ROLE_admin")
.antMatchers("/syslog.html", "/sysuser.html")
//这两种方式写法是等同的
// .hasAnyRole("admin")
.hasAnyAuthority("ROLE_admin")
.anyRequest().authenticated();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("123456"))
.roles("user")
.and()
.withUser("admin")
.password(passwordEncoder().encode("123456"))
// .authorities("/syslog", "/sysuser")
.roles("admin")
.and()
.passwordEncoder(passwordEncoder()); //配置加密方式
}
//加密方式
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
application.properties
#security
spring.security.loginType=html
2.自定义登录成功处理的测试
1)json数据返回
application.properties
#security
spring.security.loginType=json
-
浏览器打开
http://localhost:8080/sysuser访问用户管理html
image.png -
被拦截到登录页
image.png -
以admin用户登录,登录成功,返回我之前定义的json数据
image.png
2)html页面返回
application.properties
#security
spring.security.loginType=html
前几步一样,但是最后登录成功后不会返回json而是直接跳转我们想要访问的用户管理html页面,如下:

3.自定义登录失败处理的实现
很多操纵都是类似的,不多做赘述了。
MyAuthenticationFailureHandler.java
package com.grj.securityDemo.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.grj.securityDemo.model.MyResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* securit登录成功自定义处理类
* @Author: grj
* @Date: 2020/6/2 20:25
*/
@Component
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
//从application.properties读取值存入
@Value("${spring.security.loginType}")
private String loginType;
private static ObjectMapper mapper = new ObjectMapper();
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
if(loginType.equalsIgnoreCase("json")) {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(mapper.writeValueAsString(MyResponse.getFailureInstance()));
}else {
//会跳转到登录页
super.onAuthenticationFailure(request,response,exception);
}
}
}
SecurityConfig.java部分代码
http.formLogin()
.loginPage("/login.html")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/login")
// .failureForwardUrl("/login.html")
.failureHandler(myAuthenticationFailureHandler)
//注意defaultSuccessUrl和successHandler只能存在一种处理方式
// .defaultSuccessUrl("/index")
.successHandler(myAuthenticationSuccessHandler)
.and()
.authorizeRequests()
.antMatchers("/login.html", "/login").permitAll()
.antMatchers("/pageA.html", "pageB.html")
.hasAnyAuthority("ROLE_user", "ROLE_admin")
.antMatchers("/syslog.html", "/sysuser.html")
//这两种方式写法是等同的
// .hasAnyRole("admin")
.hasAnyAuthority("ROLE_admin")
.anyRequest().authenticated();
4.自定义登录失败处理的测试
1)html
没啥好说的,就是登录失败还是跳回登录页
2)json
会出现以下的效果

5.总结
实现登录验证结果的自定义处理,可以根据需求使用json或者html页面做出相应的反应,比如其后端分离常用的是json返回,并加以回应处理,一般前后端不分离直接采用页面跳转就可以了,当然根据实际需求选择。


