SpringSecurity之认证

1. 盐值加密

1. 原理概述

SpringSecurity使用的是随机盐值加密

随机盐是在对密码摘要之前随机生成一个盐,并且会把这个盐的明文和摘要拼接一起保存

举个例子:密码是pwd,随机盐是abc,pwd+abc摘要后的信息是xyz,最后保存的密码就是abcxyz

随机盐 同一个密码,每次摘要后的结果都不同,但是可以根据摘要里保存的盐来校验摘要和明文密码是否匹配

在hashpw函数中, 我们可以看到以下这句

real_salt = salt.substring(off + 3, off + 25);

说明我们真正用于盐值加密的是real_salt, 从而保证了我们生成随机盐值也能再校验时通过相同的规则得到需要的结果

2. 使用说明

1. 加密

  • 首先我们要在SpringSecurity的配置文件中配置密码的加密方式
/密码使用盐值加密 BCryptPasswordEncoder
//BCrypt.hashpw() ==> 加密
//BCrypt.checkpw() ==> 密码比较
//我们在数据库中存储的都是加密后的密码, 只有在网页上输入时是明文的
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
  • 然后在我们的用户管理实现类中实现向数据库添加新用户(注册功能) 时对密码加密
@Override
public Integer addUser(UserDTO user) {
//先查看要添加的用户是否在数据库中
String username = user.getUsername();
UserDTO userByUsername = getUserByUsername(username);
//如果待插入的用户存在在数据库中, 插入0条
if (null != userByUsername) {
return 0;
} else {
//不存在, 则插入用户
//先对密码进行盐值加密, SpringSecurity中使用的是随机盐值加密
String hashpw = passwordEncoder.encode(user.getPassword());
user.setPassword(hashpw);
return userMapper.addUser(user);
}
}
  • 在我们提交用户名和密码的表单之后, 在数据库中差看我们存储的用户名和密码

可以看到, 密码与我们明文输入的 123456 完全不同

  • 这里要注意一点, 设计数据库时密码不要少于60位!

2. 认证

讲在前面的话:

认证的配置类的 setFilterProcessesUrl("/login") (这里是自定义过滤器的配置, form方式与其一致)中, url只是我们提交表单或者ajax请求的地址, 不需要在Controller中注册, 注册了PostMapping也不会走, 但是会走Get方式, 此时SpringSecurity不会帮我们认证(认为是不安全的提交方式)

1. 页面成功跳转的坑

页面成功跳转有两个方法

  • defaultSuccessUrl
  • successForwardUrl

前者是重定向, 后者是转发, 由于转发地址栏不会变化, 而我们SpringSecurity要求提交表单的方法必须为post(此处也是大坑!切记!), 因此请求类型后者依然为post

此时, 如果我们在addViewControllers中配置了首页的路径映射, 同时我们成功后要跳转到首页, 使用后一种方法就会报405错误, 提示我们请求类型错误

有两种解决方法

  • 使用第一种方法, 可以接受一个get请求的url
  • 配置一个Controller进行Post方式的页面跳转

2. 使用验证码校验的坑

验证码校验我在之前的文章中提到过, 这里就不再赘述

主要说说验证码随认证一起提交的坑

设置提交的url和我们login的form url一致, 注意此时一定要用GET请求提交表单!

如果我们使用相同的url在controller层试图进行校验并重定向跳转, 可以发现根本就不会走我们的controller!

同时, 我们试图用拦截器拦截响应的url, 并在表单提交之前拦截来下进行校验, 也失败了

说明SpringSecurity自己的校验的优先级相当的高

此时, 我们只能实现一个认证成功的处理器来处理我们的验证码

  • 实现AuthenticationSuccessHandler接口并用SpringBoot托管
package com.wang.spring_security_framework.config.SpringSecurityConfig;

import com.wang.spring_security_framework.service.CaptchaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; //登录成功处理, 用于比对验证码
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
CaptchaService captchaService; @Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//校验验证码
Boolean verifyResult = captchaService.versifyCaptcha(request.getParameter("token"),
request.getParameter("inputCode"));
if (verifyResult) {
response.sendRedirect("/index");
} else {
response.sendRedirect("/toLoginPage");
}
}
}
  • 在SpringSecurity的配置类中使用我们自己定义的处理类
@Override
protected void configure(HttpSecurity http) throws Exception {
//指定自定义的登录页面, 表单提交的url, 以及成功后的处理器
http.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/toLoginPage")
.loginProcessingUrl("/login")
.successHandler(loginSuccessHandler)
.and()
.csrf()
.disable();
}

此处有个大坑, 如果设置了成功的处理类, 我们就千万不要在配置类中写成功跳转的方法了, 这样会覆盖掉我们的成功处理器!

3. 前端用ajax请求并附加验证码校验

此处为天坑! 足足费了我快一天半才爬出来! 简直到处都是坑, 还有一个问题没解决...

总之不推荐这么干, 主要指用AJAX请求再用后台跳转

  • 首先, 我们要明确一点, AJAX会刷新局部页面, 这就造成了重定向请求没问题, 但是页面不跳转, 看请求头我们会发现url还是当前页面
  • 其次, SpringSecurity的认证是用request.getparameter()读出的, 因此无法解析AJAX请求传来的JSON, 我们要自己写过滤器解析
  • 最后, SpringSecurity在认证过滤器结束后会关闭request的Stream, 导致我们无法取出前端发来的数据, 需要我们再添加一个request, 再在成功的处理器中获得request中的对象

好了, 让我们来看看这个坑吧!

  • 前端代码

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="UTF-8">
    <title>登录界面</title>
    <link th:href="@{css/default.css}" rel="stylesheet" type="text/css"/>
    <!--必要样式-->
    <link th:href="@{css/styles.css}" rel="stylesheet" type="text/css"/>
    <link th:href="@{css/demo.css}" rel="stylesheet" type="text/css"/>
    <link th:href="@{css/loaders.css}" rel="stylesheet" type="text/css"/>
    </head>
    <body>
    <div class='login'>
    <div class='login_title'>
    <span>登录</span>
    </div>
    <div class='login_fields'>
    <!-- <form action="/login" method="post">-->
    <div class='login_fields__user'>
    <div class='icon'>
    <img alt="" src='img/user_icon_copy.png'>
    </div>
    <input name="username" placeholder='用户名' maxlength="16" type='text' autocomplete="off"/>
    <div class='validation'>
    <img alt="" src='img/tick.png'>
    </div>
    </div>
    <div class='login_fields__password'>
    <div class='icon'>
    <img alt="" src='img/lock_icon_copy.png'>
    </div>
    <input name="password" placeholder='密码' maxlength="16" type='text' autocomplete="off">
    <div class='validation'>
    <img alt="" src='img/tick.png'>
    </div>
    </div>
    <div class='login_fields__password'>
    <div class='icon'>
    <img alt="" src='img/key.png'>
    </div>
    <input name="inputCode" placeholder='验证码' maxlength="4" type='text' autocomplete="off">
    <div class='validation' style="opacity: 1; top: -3px;">
    <!-- 当用户链接时,void(0)计算为0,用户点击不会发生任何效果 -->
    <a href="javascript:void(0);" title="点击更换验证码">
    <!--this参数, 返回当前的DOM元素-->
    <img src="" alt="更换验证码" id="imgVerify" onclick="getVerify(this)">
    </a>
    </div>
    </div>
    <div class='login_fields__submit'>
    <input type='button' value='登录'>
    </div>
    <div>
    <!--通过隐藏域传递值, 在下面的验证码点击事件中, 将值绑定过来, 这样就可以获得最新的验证码对应的值了!-->
    <input name="token" value="" type="hidden" id="token">
    </div>
    <!-- </form>-->
    </div>
    </div> <link th:href="@{layui/css/layui.css}" rel="stylesheet" type="text/css"/> <script type="text/javascript" th:src="@{js/jquery.min.js}"></script>
    <script type="text/javascript" th:src="@{js/jquery-ui.min.js}"></script>
    <script type="text/javascript" th:src="@{layui/layui.js}"></script>
    <script type="text/javascript" th:src="@{js/Particleground.js}"></script>
    <script type="text/javascript" th:src="@{js/Treatment.js}"></script>
    <script type="text/javascript" th:src="@{js/jquery.mockjax.js}"></script>
    <script type="text/javascript">
    $(document).keypress(function (e) {
    // 回车键事件 ascii 13
    if (e.which === 13) {
    $('input[type="button"]').click();
    }
    }); //粒子背景特效
    $('body').particleground({
    dotColor: '#39db24',
    lineColor: '#133b88'
    });
    $('input[name="password"]').focus(function () {
    $(this).attr('type', 'password');
    });
    $('input[type="text"]').focus(function () {
    $(this).prev().animate({'opacity': '1'}, 200);
    });
    $('input[type="text"],input[type="password"]').blur(function () {
    $(this).prev().animate({'opacity': '.5'}, 200);
    });
    $('input[name="username"],input[name="password"]').keyup(function () {
    var Len = $(this).val().length;
    if (!$(this).val() === '' && Len >= 5) {
    $(this).next().animate({
    'opacity': '1',
    'right': '30'
    }, 200);
    } else {
    $(this).next().animate({
    'opacity': '0',
    'right': '20'
    }, 200);
    }
    }); layui.use('layer', function () {
    //非空验证
    $('input[type="button"]').click(function () {
    let login = $('input[name="username"]').val();
    let pwd = $('input[name="password"]').val();
    let code = $('input[name="inputCode"]').val();
    let token = $('input[name="token"]').val();
    let JsonData = {"username": login, "password": pwd, "inputCode": code, "token": token};
    if (login === '') {
    ErroAlert('请输入您的账号');
    } else if (pwd === '') {
    ErroAlert('请输入密码');
    } else if (code === '' || code.length !== 4) {
    ErroAlert('输入验证码');
    } else {
    let url = "/login";
    $.ajaxSetup({
    url: url,
    type: "post",
    dataType: "json",
    contentType: "application/json;charset=utf-8",
    complete: function (XMLHttpRequest, textStatus) {
    console.log(XMLHttpRequest.status);
    //通过XMLHttpRequest获取响应头
    let redirect = XMLHttpRequest.getResponseHeader("REDIRECT");
    console.log(redirect);
    if (redirect === "REDIRECT") {
    let win = window;
    while (win != win.top) {
    win = win.top;
    }
    win.location.href = XMLHttpRequest.getResponseHeader("CONTEXTPATH");
    }
    }
    });
    $.ajax({
    data: JSON.stringify(JsonData),
    success: function () {
    console.log("进入回调函数了!");
    },
    error: function (xhr, textStatus, errorThrown) {
    alert("进入error---");
    alert("状态码:"+xhr.status);
    alert("状态:"+xhr.readyState); //当前状态,0-未初始化,1-正在载入,2-已经载入,3-数据进行交互,4-完成。
    alert("错误信息:"+xhr.statusText );
    alert("返回响应信息:"+xhr.responseText );//这里是详细的信息
    alert("请求状态:"+textStatus);
    alert(errorThrown);
    alert("请求失败");
    }
    });
    }
    });
    });
    //获得img对象
    let imgVerify = $("#imgVerify").get(0);
    //$(function())等同于$(document).ready(function()) ==> 页面加载完毕之后, 才执行函数
    $(function () {
    getVerify(imgVerify);
    }); //onclick时间绑定的getVerify函数
    function getVerify(obj) {
    $.ajax({
    type: "POST",
    url: "/captcha",
    success: function (result) {
    obj.src = "data:image/jpeg;base64," + result.img;
    $("#token").val(result.token);
    }
    });
    }
    </script> </body>
    </html>
    • 这里主要是$.ajaxSetup()方法, 可以定义全局的(同一个函数中的)ajax的一些参数, 尤其是里面的complete方法, 是在全部执行完之后调用的, 为了能强行跳转AJAX, 我们要天剑请求头, 我们在后面的后端代码中可以看到
    • 我们还需要写$.ajax()传递数据, 注意, json数据就算我们用json的格式写了, 还是要用JSON.stringify()方法转一下, 否则传到后端的不是JSON!
    • 此处有一个没有解决的问题, 不知道为什么不会走成功的回调函数, 只会走失败的回调函数
  • 自定义认证过滤器

    package com.wang.spring_security_framework.config.SpringSecurityConfig;
    
    import com.alibaba.fastjson.JSON;
    import org.springframework.http.MediaType;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Map; //默认的提取用户名和密码是通过 request.getParameter() 方法来提取的, 所以通过form我们可以提取到
    //但是如果我们用ajax传递的话, 就提取不到了, 需要自己写过滤器!
    //这里不能写 @Component, 因为我们要在SpringSecurity配置类中注册 myCustomAuthenticationFilter 并配置
    //否则会爆出重名的Bean!
    public class MyCustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    //如果request请求是一个json同时编码方式为UTF-8
    if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)
    || request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
    UsernamePasswordAuthenticationToken authRequest = null; Map<String, String> authenticationBean = null;
    try (InputStream inputStream = request.getInputStream()) {
    //将JSON转为map
    authenticationBean = JSON.parseObject(inputStream, Map.class);
    //将用户名和密码放入 authRequest
    authRequest = new UsernamePasswordAuthenticationToken(
    authenticationBean.get("username"), authenticationBean.get("password"));
    System.out.println(authenticationBean);
    } catch (IOException e) {
    e.printStackTrace();
    //出现IO异常, 放空的用户信息
    authRequest = new UsernamePasswordAuthenticationToken("", "");
    } finally {
    //将请求 request 和解析后的用户信息 authRequest 放入userDetails中
    setDetails(request, authRequest);
    //将我们前端传递的JSON对象继续放在request里传递, 这样我们就可以在认证成功的处理器中拿到它了!
    request.setAttribute("authInfo", authenticationBean); return this.getAuthenticationManager().authenticate(authRequest);
    }
    } else {
    return super.attemptAuthentication(request, response);
    }
    }
    }
    • 这里还是要强调一点, @Component会自动注册内部的全部的方法, 如果我们在别的地方@Bean了方法, 会报一些奇怪的错误, 本质上是冲突了!
    • 此处我们是用FastJSON将JSON转为了Map
  • 认证成功处理器

    package com.wang.spring_security_framework.config.SpringSecurityConfig;
    
    import com.alibaba.fastjson.JSON;
    import com.wang.spring_security_framework.service.CaptchaService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContext;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.stereotype.Component; import javax.servlet.ServletException;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map; //登录成功处理
    //我们不能在这里获得request了, 因为我们已经在前面自定义了认证过滤器, 做完后SpringSecurity会关闭inputStream流
    @Component
    public class LoginSuccessHandler implements AuthenticationSuccessHandler {
    @Autowired
    CaptchaService captchaService; @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
    HttpServletResponse response,
    Authentication authentication) throws IOException, ServletException {
    //我们从自定义的认证过滤器中拿到的authInfo, 接下来做验证码校验和跳转
    Map<String, String> authInfo = (Map<String, String>) request.getAttribute("authInfo");
    System.out.println(authInfo);
    System.out.println("success!");
    String token = authInfo.get("token");
    String inputCode = authInfo.get("inputCode"); //校验验证码
    Boolean verifyResult = captchaService.versifyCaptcha(token, inputCode);
    System.out.println(verifyResult);
    if (verifyResult) {
    HashMap<String, String> map = new HashMap<>();
    map.put("url", "/index");
    System.out.println(map);
    String VerifySuccessUrl = "/index";
    response.setHeader("Content-Type", "application/json;charset=utf-8");
    // response.setContentType("application/json;charset=utf-8");
    response.addHeader("REDIRECT", "REDIRECT");
    response.addHeader("CONTEXTPATH", VerifySuccessUrl);
    } else {
    String VerifyFailedUrl = "/toRegisterPage";
    response.setHeader("Content-Type", "application/json;charset=utf-8");
    // response.setContentType("application/json;charset=utf-8");
    response.addHeader("REDIRECT", "REDIRECT");
    response.addHeader("CONTEXTPATH", VerifyFailedUrl);
    // response.sendRedirect("/toRegisterPage");
    }
    }
    }
    • 这里需要注意一点, 我们需要从前面的Request拿到对象
    • addHeader里面我们为了重定向, 添加了响应头, 可以和前端的ajaxSetup对应着看
  • SpringSecurity配置类

    package com.wang.spring_security_framework.config;
    
    import com.wang.spring_security_framework.config.SpringSecurityConfig.LoginSuccessHandler;
    import com.wang.spring_security_framework.config.SpringSecurityConfig.MyCustomAuthenticationFilter;
    import com.wang.spring_security_framework.service.UserService;
    import com.wang.spring_security_framework.service.serviceImpl.UserDetailServiceImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; //SpringSecurity设置
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserService userService;
    @Autowired
    UserDetailServiceImpl userDetailServiceImpl;
    @Autowired
    LoginSuccessHandler loginSuccessHandler; //授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    //指定自定义的登录页面, 表单提交的url, 以及成功后的处理器
    http.formLogin()
    .loginPage("/toLoginPage")
    .failureForwardUrl("/index")
    .and()
    .csrf()
    .disable();
    // .failureForwardUrl();
    //注销 //设置过滤器链, 添加自定义过滤器
    http.addFilterAt(
    myCustomAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class
    );
    //允许iframe
    // http.headers().frameOptions().sameOrigin();
    } //注册自定义过滤器
    @Bean
    MyCustomAuthenticationFilter myCustomAuthenticationFilter() throws Exception {
    MyCustomAuthenticationFilter filter = new MyCustomAuthenticationFilter();
    //设置过滤器认证管理
    filter.setAuthenticationManager(super.authenticationManagerBean());
    //设置filter的url
    filter.setFilterProcessesUrl("/login");
    //设置登录成功处理器
    filter.setAuthenticationSuccessHandler(loginSuccessHandler);
    //TODO 设置登录失败处理器 return filter;
    } //密码使用盐值加密 BCryptPasswordEncoder
    //BCrypt.hashpw() ==> 加密
    //BCrypt.checkpw() ==> 密码比较
    //我们在数据库中存储的都是加密后的密码, 只有在网页上输入时是明文的
    @Bean
    public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
    } }
    • 这里主要干了两件事

      • 注册了我们自定义的过滤器
      • 在过滤器链中注册我们的过滤器

4. 后端只提供JSON让前端进行跳转

这里主要修改了两处, 我们的成功处理器返回的是一个封装好的JSON, 同时我们在ajax的回调函数中写了页面跳转的逻辑

  • 成功处理器

    package com.wang.spring_security_framework.config.SpringSecurityConfig;
    
    import com.alibaba.fastjson.JSON;
    import com.wang.spring_security_framework.service.CaptchaService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContext;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.stereotype.Component; import javax.servlet.ServletException;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.HashMap;
    import java.util.Map; //登录成功处理
    //我们不能在这里获得request了, 因为我们已经在前面自定义了认证过滤器, 做完后SpringSecurity会关闭inputStream流
    @Component
    public class LoginSuccessHandler implements AuthenticationSuccessHandler {
    @Autowired
    CaptchaService captchaService; @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
    HttpServletResponse response,
    Authentication authentication) throws IOException, ServletException {
    //我们从自定义的认证过滤器中拿到的authInfo, 接下来做验证码校验和跳转
    Map<String, String> authInfo = (Map<String, String>) request.getAttribute("authInfo");
    System.out.println(authInfo);
    System.out.println("success!");
    String token = authInfo.get("token");
    String inputCode = authInfo.get("inputCode"); //校验验证码
    Boolean verifyResult = captchaService.versifyCaptcha(token, inputCode);
    System.out.println(verifyResult); Map<String, String> result = new HashMap<>();
    if (verifyResult) {
    HashMap<String, String> map = new HashMap<>();
    map.put("url", "/index");
    System.out.println(map);
    String VerifySuccessUrl = "/index";
    response.setHeader("Content-Type", "application/json;charset=utf-8");
    result.put("code", "200");
    result.put("msg", "认证成功!");
    result.put("url", VerifySuccessUrl);
    PrintWriter writer = response.getWriter();
    writer.write(JSON.toJSONString(result));
    } else {
    String VerifyFailedUrl = "/toLoginPage";
    response.setHeader("Content-Type", "application/json;charset=utf-8");
    result.put("code", "201");
    result.put("msg", "验证码输入错误!");
    result.put("url", VerifyFailedUrl);
    PrintWriter writer = response.getWriter();
    writer.write(JSON.toJSONString(result));
    }
    }
    }
    • 这里只需要注意一点, 及时ContentType一定要加上, 防止出现奇怪的响应头的问题
  • 前端修改, 这里删除了complete方法, 添加了回调函数, 因此我们只放出ajax

    $.ajax({
    data: JSON.stringify(JsonData),
    success: function (data) {
    alert("进入success---");
    let code = data.code;
    let url = data.url;
    let msg = data.msg;
    if (code == 200) {
    alert(msg);
    window.location.href = url;
    } else if (code == 201) {
    alert(msg);
    window.location.href = url;
    } else {
    alert("未知错误!")
    }
    },
    error: function (xhr, textStatus, errorThrown) {
    alert("进入error---");
    alert("状态码:" + xhr.status);
    alert("状态:" + xhr.readyState); //当前状态,0-未初始化,1-正在载入,2-已经载入,3-数据进行交互,4-完成。
    alert("错误信息:" + xhr.statusText);
    alert("返回响应信息:" + xhr.responseText);//这里是详细的信息
    alert("请求状态:" + textStatus);
    alert(errorThrown);
    alert("请求失败");
    }
    });

5. 失败处理器

认证失败的处理器, 主要是三个部分, 失败处理器, 配置类中自定义过滤器添加失败处理器, 以及前端添加回调函数的失败处理器的跳转逻辑

其中配置类和前端都非常简单, 我们这里只贴出失败处理器供大家参考

package com.wang.spring_security_framework.config.SpringSecurityConfig;

import com.alibaba.fastjson.JSON;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap; //认证失败的处理器
@Component
public class LoginFailHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
HashMap<String, String> result = new HashMap<>();
String AuthenticationFailUrl = "/toRegisterPage";
response.setHeader("Content-Type", "application/json;charset=utf-8");
result.put("code", "202");
result.put("msg", "认证失败!密码或用户名错误!即将跳转到注册页面!");
result.put("url", AuthenticationFailUrl);
PrintWriter writer = response.getWriter();
writer.write(JSON.toJSONString(result));
}
}

3. 写在最后的话

  • 本文其实不算是教程, 只是个人在练习SpringSecurity进行认证的踩坑以及总结
  • 当然, 附加验证码校验应该写在token的自定义类中, 这里我偷懒了...有机会再补上吧
  • 请忽视我丑陋的AJAX回调信息, 这里的标准做法是定义返回的信息类

SpringSecurity之认证的更多相关文章

  1. springsecurity basic 认证

    Basic Access Authentication scheme是在HTTP1.0提出的认证方法,它是一种基于challenge/response的认证模式,针对特定的realm需要提供用户名和密 ...

  2. spring-security 登陆认证之初次探究

    首先,希望还对 spring-security框架完全不懂的新手 下载下Git源码. 引入到项目中.这个短文就是边看源码边聊的.也会启动下项目验证自己的推想. 一.登陆认证的登陆配置项 <for ...

  3. springSecurity自定义认证配置

    上一篇讲了springSecurity的简单入门的小demo,认证用户是在xml中写死的.今天来说一下自定义认证,读取数据库来实现认证.当然,也是非常简单的,因为仅仅是读取数据库,权限是写死的,因为相 ...

  4. SpringBoot+SpringSecurity+Thymeleaf认证失败返回错误信息踩坑记录

    Spring boot +Spring Security + Thymeleaf认证失败返回错误信息踩坑记录 步入8102年,现在企业开发追求快速,Springboot以多种优秀特性引领潮流,在众多使 ...

  5. SpringSecurity——基于Spring、SpringMVC和MyBatis自定义SpringSecurity权限认证规则

    本文转自:https://www.cnblogs.com/weilu2/p/springsecurity_custom_decision_metadata.html 本文在SpringMVC和MyBa ...

  6. SpringSecurity 获取认证信息 和 认证实现

    JdbcDaoImpl 实现获取认证信息 PasswordEncoder 实现具体认证过程

  7. SpringSecurity(1)---认证+授权代码实现

    认证+授权代码实现 Spring Security是 一种基于 Spring AOP 和 Servlet 过滤器的安全框架.它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和 ...

  8. SpringSecurity操作指南-基于Spring、SpringMVC和MyBatis自定义SpringSecurity权限认证规则

  9. SpringSecurity04 利用JPA和SpringSecurity实现前后端分离的认证和授权

    1 环境搭建 1.1 环境说明 JDK:1.8 MAVEN:3.5 SpringBoot:2.0.4 SpringSecurity:5.0.7 IDEA:2017.02旗舰版 1.2 环境搭建 创建一 ...

随机推荐

  1. lumen发送邮件配置

    composer.json 增加 "illuminate/mail":"5.6"composer update -vvvconfig 目录新建mail.php ...

  2. day70:Vue:Git&路飞学城页面效果

    目录 1.Git 2.路飞学城项目页面效果 0.安装elements UI 1.顶部导航栏效果 2.轮播图效果 1.Git 什么是git?分布式版本管理工具 1.git操作 # 1 创建git本地仓库 ...

  3. Linux文件系统和管理-2文件操作命令(上)

    文件操作命令 文件 文件也包括目录 目录是一种特殊的文件 目录 一个目录名分成两部分 所在目录 dirname 父目录的路径 文件名 basename 本身就是两个命令 [root@C8-1 misc ...

  4. git学习(三) git的分支操作

    git的分支操作 软件项目中启动一套单独的开发线的方法,可以很好的避免版本兼容开发的问题,避免不同版本之间的相互影响,封装一个开发阶段,解决bug的时候新建分支,用于对该bug的研究: git中跟分支 ...

  5. Dockerfile 笔记

    Dockerfile   ARGARG <name>[=<default value>]The ARG instruction defines a variable that ...

  6. non-local denoising methods

    NL-Means算法 在噪声先验为高斯噪声的基础上, 进行non-local的平均,在2005年由Baudes提出,该算法使用自然图像中普遍存在的冗余信息来去噪声.与常用的双线性滤波.中值滤波等利用图 ...

  7. ams1117资料汇总

    AMS1117系列稳压器有可调版与多种固定电压版,设计用于提供1A输出电流且工作压差可低至1V.在最大输出电流时,AMS1117器件的最小压差保证不超过1.3V,并随负载电流的减小而逐渐降低. AMS ...

  8. Dev中配置graphcis.h

    下载地址:http://winbgim.codecutter.org/ 搞得自己有点奔溃 没成功 尝试了全网的所以的方法都没成功

  9. Learn day10 锁

    1.锁 # ### 锁 from multiprocessing import Lock,Process import json,time """ # 创建一把锁 loc ...

  10. Android Studio的第一次经历

    第一个简单APP的制作是从xml开始的,通过在java新建一个empty  activity,并在layout里找到对应的xml文件进行编写.每编写一个xml就要事先新建 一个对应的empty  ac ...