前言
前面所做的SpringSecurity登录处理都是基于页面的,现在流行前后端分离开发,没有使用表单,数据交互都是使用JSON,下面来看看怎么处理Ajax请求,移动端的请求验证也适用。
一、修改原login页面的form表单
这是原来的:
<form th:action="@{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<div><label> code: <input type="text" name="captcha"/> </label>
<img src="/getCode" id="code" alt="验证码" onclick="refresh(this)"/>
</div>
<label> <input type="hidden" name="uuid" id="uuid"/> </label>
<div><input type="submit" value="Sign In"/></div>
</form>
我们要修改成这样:
<form >
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<div><label> code: <input type="text" name="captcha"/> </label>
<img src="/getCode" id="code" alt="验证码" onclick="refresh(this)"/>
</div>
<label> <input type="hidden" name="uuid" id="uuid"/> </label>
<div><a href="javascript:login();" >登录</a></div>
</form>
// js
let login=()=>{
let username = $("input[name = 'username']");
let password = $("input[name = 'password']");
let captcha = $("input[name = 'captcha']");
let uuid = $("input[name = 'uuid']");
$.ajax({
type : "POST",
url : "/login",
data : {
"username" : username.val(),
"password" : password.val(),
"captcha" : captcha.val(),
"uuid" : uuid.val()
},
success : function(data) {
console.log(data);
if (data.code === 1) {
//登录成功
window.location.href = "/";
} else {
alert("登录失败");
}
},
error : function(data) {
alert("错误的用户或密码");
},
beforeSend : function() {
if (username.val() === "") {
alert("请输入用户名!");
username.focus();
return false;
}
if (password.val() === "") {
alert("请输入密码!");
password.focus();
return false;
}
}
});
};
二、在controller中新增login方法,用来代替spring security的登录界面
/**
* 这个接口用来代替spring security的登录界面
*
* @Author zhangchao
@Date 2020/3/21 02:44
* @param
* @retrun java.lang.Object
**/
@GetMapping("/login_page")
public Object loginpage(){
return new ResponseMessage(false,"0","还未登录,请登录!",null);
}
三、在Spring Security配置类SecurityConfig中加入login_page
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() //需要授权的请求
.antMatchers("/login","/login_page","/home","/getCode").permitAll() //过滤不需要认证的路径
.antMatchers("/auth/**").permitAll() //过滤不需要认证的路径
.anyRequest().authenticated() //对任何一个请求,都需要认证
.and() //完成上一个配置,进行下一步配置
//.httpBasic();
.formLogin() //配置表单登录
.loginPage("/login_page") //设置登录页面
.loginProcessingUrl("/login")
.successHandler(successHandler) /* 设置成功处理器 */
.failureHandler(failureHandler) /* 设置失败处理器*/
.and()
.logout() //登出
.logoutSuccessUrl("/home"); //设置退出页面
// 添加验证码过滤器
http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
// 禁用CSRF防护
http.csrf().disable();
http.addFilterBefore(jwtAuthorizationTokenFilter,UsernamePasswordAuthenticationFilter.class);
}
另外还要修改登录成功处理器LoginSuccessHandler:
/**
* @author ZHANGCHAO
* @date 2020/3/13 9:35
* @since 1.0.0
*/
@Slf4j
@Component
public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Autowired
private JwtTokenUtil jwtTokenUtil;
private static final ObjectMapper mapper = new ObjectMapper();
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
log.info("登录成功!");
/* 默认:会帮我们跳转到上一次请求的页面上 */
//super.onAuthenticationSuccess(request, response, authentication);
//生成token
String token = jwtTokenUtil.makeToken((UserDetails) authentication.getPrincipal());
log.info("用户 [{}] 登录成功!",authentication.getName());
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
PrintWriter writer = response.getWriter();
// writer.write("{\"status\":\"ok\",\"msg\":\"登录成功\",\"token\":\""+token+"\"}");
writer.write(mapper.writeValueAsString(new ResponseMessage(true, "1", "操作成功!",token)));
writer.flush();
writer.close();
}
}
由于配置的loginPage("/login_page")跳转的页面是json数据,不是一个页面,所以我们需要自定义一个AuthenticationEntryPoint,
四、自定义认证入口点类UnauthorizedEntryPoint,继承AuthenticationEntryPoint
/**
* @program sweet-dream
* @description:
* @author: zhangchao
* @date: 2020/03/21 03:03
* @since: 1.0.0
*/
@Component
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
if (isAjaxRequest(request)){
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,e.getMessage());
} else {
response.sendRedirect("/login");
}
}
private boolean isAjaxRequest(HttpServletRequest request){
return request.getHeader("X-Requested-With") != null && request.getHeader("X-Requested-With").equals("XMLHttpRequest");
}
}
AuthenticationEntryPoint 用来解决匿名用户访问无权限资源时的异常!配置该类能够对匿名用户进行拦截并返回对应数据。当用户访问被保护资源的时候,在过滤器中被发现是匿名用户则会由这个类进行处理。
AuthenticationEntryPoint
简介
AuthenticationEntryPoint
是Spring Security Web
一个概念模型接口,顾名思义,他所建模的概念是:“认证入口点”。
它在用户请求处理过程中遇到认证异常时,被ExceptionTranslationFilter
用于开启特定认证方案(authentication schema
)的认证流程。
该接口只定义了一个方法 :
void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException;
这里参数request
是遇到了认证异常authException
用户请求,response
是将要返回给客户的相应,方法commence
实现,也就是相应的认证方案逻辑会修改response
并返回给用户引导用户进入认证流程。
五、配置CRSF防护
-
在SpringSecurity配置类SecurityConfig中去掉禁止CRSF防护的代码,注入UnauthorizedEntryPoint
// 禁用CSRF防护 // http.csrf().disable(); /* 关闭csrf,否则会拦截ajax请求 */ http.exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint); /* 用来解决匿名用户访问无权限资源时的异常 */
-
在登录页面login.html中开启CRSF防护,crsf开启,会拦截ajax请求所以需要在请求中加入crsf参数
head里加:
<head> <meta name="_csrf" th:content="${_csrf.token}"/> <meta name="_csrf_header" th:content="${_csrf.parameterName}"/> <title>Spring Security Example </title> <script src="https://cdn.staticfile.org/jquery/3.3.1/jquery.min.js"></script> </head>
js里加:
$.ajax({ type : "POST", url : "/login", data : { "username" : username.val(), "password" : password.val(), "captcha" : captcha.val(), "uuid" : uuid.val(), [[${_csrf.parameterName}]]: [[${_csrf.token}]] },
六、启动项目测试
登录成功!
以上