个性化用户认证流程
一.自定义登录页面
1.创建html文件,(Springboot项目html文件路径在resources/static/imooc-signIn.html),该表单登录请求为post请求,具体url为 /authentication/form
<form action="/authentication/form" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2">
<button type="submit">登录</button>
</td>
</tr>
</table>
</form>
2.配置文件中 SecurityConfig中的 configure方法中,更改为:
http.formLogin()
.loginPage("/imooc-signIn.html") //所有请求跳转至登录页面
.loginProcessingUrl("/authentication/form") //UsernamePasswordAuthenticationFilter处理该请求
.and()
.authorizeRequests()
.antMatchers("/imooc-signIn.html").permitAll() //对该请求不进行身份认证
.anyRequest()
.authenticated()
.and().csrf().disable(); //disable掉跨站防护功能
3.前两步的登录适用于返回html页面,但在前后端分离的项目中,返回的不一定是一个html页面,也有可能是一段json代码。故对代码进行改造。
4.新增一个接口
@RestController
@Slf4j
public class SecurityController {
private RequestCache requestCache = new HttpSessionRequestCache();
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
/**
* 当需要身份认证时,跳转到这里
*/
@RequestMapping("/authentication/require")
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
SavedRequest savedRequest = requestCache.getRequest(request,response);
if(savedRequest!=null){
String targetUrl = savedRequest.getRedirectUrl();
log.info("引发跳转的请求是:"+targetUrl);
if(StringUtils.endsWithIgnoreCase(targetUrl,".html")){
redirectStrategy.sendRedirect(request,response,targetUrl);
}
}
return new SimpleResponse("访问的服务需要身份认证,请引导用户至登录页");
}
}
5.其中返回对象的结构
@Data
public class SimpleResponse {
private Object content;
public SimpleResponse(Object content) {
this.content = content;
}
}
6.SecurityConfig中的 configure方法中,更改为:
http.formLogin()
.loginPage("/authentication/require")
.loginProcessingUrl("/authentication/form")
.and()
.authorizeRequests()
.antMatchers("/imooc-signIn.html","/authentication/require").permitAll()
.anyRequest()
.authenticated()
.and().csrf().disable();
二.如何让用户可以自己去配置登录页面
-
创建html文件
-
在application.yml中进行配置
imooc: security: browser: loginPage: /imooc-signIn.html
-
创建对象,
@Data @ConfigurationProperties(prefix = "imooc.security") public class SecurityProperties { private BrowserProperties browser = new BrowserProperties(); } @Data public class BrowserProperties { private String loginPage = "/demo-signIn.html"; }
在SecurityConfig配置类上使用注解@EnableConfigurationProperties(SecurityProperties.class)将SecurityProperties进行bean加载
-
使用
@Autowired private SecurityProperties securityProperties; securityProperties.getBrowser().getLoginPage();
如果用户没有配置,则使用loginPage的默认值,如果进行了配置,则使用用户配置的html页面
三.自定义登录成功和失败流程
1.创建自定义登录成功处理器
/**
* 自定义登录成功处理
*/
@Component("imoocAuthenticationSuccessHandler")
@Slf4j
public class ImoocAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
log.info("登录成功");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(authentication));
}
}
2.创建自定义登录失败处理器
@Slf4j
@Component("imoocAuthenticationFailureHandler")
public class ImoocAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
log.info("登录失败");
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(new SimpleResponse(e.getMessage())));
}
}
3.在使用该处理器的类中注入config
@Autowired
private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler;
@Autowired
private LoginAuthenticationFailureHandler loginAuthenticationFailureHandler;
4.SecurityConfig中的 configure方法中,更改为:
http.formLogin()
.loginPage("/authentication/require")
.loginProcessingUrl("/authentication/form")
.successHandler(imoocAuthenticationSuccessHandler)
.failureHandler(loginAuthenticationFailureHandler)
.and()
.authorizeRequests()
.antMatchers(securityProperties.getBrowser().getLoginPage(),
"/authentication/require").permitAll()
.anyRequest()
.authenticated()
.and().csrf().disable();
5.如需将成功和失败处理做成可配置的,则需要如下改动
## 1. 成功和失败处理器分别继承如下接口
ImoocAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler
ImoocAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler
##2. 创建枚举,用来保存登录的方式
public enum LoginType {
REDIRECT,
JSON,
;
}
##3. 在BrowserProperties配置类中添加loginType属性
@Data
public class BrowserProperties {
private String loginPage = "/demo-signIn.html";
private LoginType loginType = LoginType.JSON;
}
##4. 成功处理器和失败处理器中进行分情况处理
成功处理器中逻辑为
if(LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){
log.info("登录成功");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(authentication));
}else {
super.onAuthenticationSuccess(request,response,authentication);
}
失败处理器中逻辑为
log.info("登录失败");
if(LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
//返回前台不需要返回所有信息,只需要返回错误信息即可
response.getWriter().write(objectMapper.writeValueAsString(new SimpleResponse(e.getMessage())));
}else {
super.onAuthenticationFailure(request,response,e);
}
##5. 在yml文件中配置属性
imooc:
security:
browser:
loginType: REDIRECT