Spring security自定义用户认证流程详解

 更新时间:2020年03月11日 14:42:01   作者:我太难了008  
这篇文章主要介绍了Spring security自定义用户认证流程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

1.自定义登录页面

(1)首先在static目录下面创建login.html

  注意:springboot项目默认可以访问resources/resources,resources/staic,resources/public目录下面的静态文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>登录页面</title>
</head>
<body>
<form action="/auth/login" method="post">
  用户名:<input type="text" name="username">
  <br/>
  密&emsp;码:<input type="password" name="password">
  <br/>
  <input type="submit" value="登录">
</form>
</body>
</html>

(2)在spring securiy配置类中做如下配置

@Override
  protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
        // 指定自定义登录页面
        .loginPage("/login.html")
        // 登录url
        .loginProcessingUrl("/auth/login")
        .and()
        .authorizeRequests()
        // 添加一个url匹配器,如果匹配到login.html,就授权
        .antMatchers("/login.html").permitAll()
        .anyRequest()
        .authenticated()
        .and()
        // 关闭spring security默认的防csrf攻击
        .csrf().disable();
  }

(3)测试

(4)存在的问题

<1>作为可以复用的登录模块,我们应该提供个性化的登录页面,也就是说不能写死只跳转到login.html。

    此问题比较好解决,使用可配置的登录页面,默认使用login.html即可。

<2> 请求跳转到login.html登录页面,貌似没有什么问题,但作为restful风格的接口,一般响应的都是json数据格式,尤其是app请求。

    解决思想:用户发起数据请求 --> security判断是否需要身份认证 ----->跳转到一个自定义的controller方法 ------>在该方法内判断是否是html发起的请求,如果是,就跳转到login.html,如果不是,响应一个json格式的数据,说明错误信息。

自定义Controller

@Slf4j
@RestController
public class LoginController {

  /**
   * 请求缓存
   */
  private RequestCache requestCache = new HttpSessionRequestCache();

  /**
   * 重定向工具类
   */
  private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

  /**
   * 如果配置的登录页就使用配置的登录面,否则使用默认的登录页面
   */
//  @Value("${xxxx:defaultLoginPage}")
//  private String standardLoginPage;
  private String standardLoginPage = "/login.html"; // 登录页

  /**
   * 用户身份认证方法
   */
  @GetMapping("/user/auth")
  @ResponseStatus(code = HttpStatus.UNAUTHORIZED) // 返回状态
  public ResponseData login(HttpServletRequest request, HttpServletResponse response) throws IOException {
    SavedRequest savedRequest = requestCache.getRequest(request, response);
    if (savedRequest != null) {
      String targetUrl = savedRequest.getRedirectUrl();
      log.info("请求是:" + targetUrl);
      // 如果请求是以html结尾
      if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
        redirectStrategy.sendRedirect(request, response, standardLoginPage);
      }
    }
    return new ResponseData("该请求需要登录,js拿到我的响应数据后,是否需要跳转到登录页面你自己看着办吧?");
  }
}

spring security给该controller的login方法授权

@Override
  protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
        // 先进controller中去
        .loginPage("/user/auth")
        // 指定自定义登录页面
        .loginPage("/login.html")
        // 登录url
        .loginProcessingUrl("/auth/login")
        .and()
        .authorizeRequests()
        // 该controller需要授权
        .antMatchers("/user/auth").permitAll()
        // 添加一个url匹配器,如果匹配到login.html,就授权
        .antMatchers("/login.html").permitAll()
        .anyRequest()
        .authenticated()
        .and()
        // 关闭spring security默认的防csrf攻击
        .csrf().disable();
  }

这样子就行了!!! 

2. 自定义登录成功处理(返回json)

(1)实现AuthenticationSuccessHandler.java

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
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;
@Slf4j
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
  @Autowired
  private ObjectMapper objectMapper;
  /**
   * Called when a user has been successfully authenticated.
   * @param request
   * @param response
   * @param authentication
   * @throws IOException
   * @throws ServletException
   */
  @Override
  public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    log.info("登录成功!!!");
    // 将登录成功的信息写到前端
    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    response.getWriter().write(objectMapper.writeValueAsString(authentication));

  }
}

(2)修改security配置类

@Autowired
  private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
        // 先进controller中去
        .loginPage("/user/auth")
        // 指定自定义登录页面
        .loginPage("/login.html")
        // 登录url
        .loginProcessingUrl("/auth/login")
        .successHandler(myAuthenticationSuccessHandler)
        .and()
        .authorizeRequests()
        // 该controller需要授权
        .antMatchers("/user/auth").permitAll()
        // 添加一个url匹配器,如果匹配到login.html,就授权
        .antMatchers("/login.html").permitAll()
        .anyRequest()
        .authenticated()
        .and()
        // 关闭spring security默认的防csrf攻击
        .csrf().disable();
  }

(3)测试

说明:authentication对象中包含的信息,会因为登录方式的不同而发生改变

3.自定义登录失败处理(返回json)

  实现AuthenticationFailureHandler.java接口即可,跟登录成败处理配置一样。

4.自定义登录成功处理逻辑

 以上的登录成功或失败的返回的都是json,但是在某些情况下,就是存在着登录成功或者失败进行页面跳转(spring security默认的处理方式),那么这种返回json的方式就不合适了。所以,我们应该做得更灵活,做成可配置的。

 对于登录成功逻辑而言只需要对MyAuthenticationSuccessHandler.java稍做修改就行,代码如下所示:

/**
 * SavedRequestAwareAuthenticationSuccessHandler spring security 默认的成功处理器
 */
@Slf4j
@Component
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
  @Autowired
  private ObjectMapper objectMapper;

  /**
   * 配置的登录方式
   */
//  @Value("${xxx:默认方式}")
  private String loginType = "JSON";
  /**
   * Called when a user has been successfully authenticated.
   */
  @Override
  public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    log.info("登录成功!!!");

    // 如果配置的登录方式是JSON,就返回json数据
    if ("JSON".equals(loginType)) {
      // 将登录成功的信息写到前端
      response.setContentType(MediaType.APPLICATION_JSON_VALUE);
      response.getWriter().write(objectMapper.writeValueAsString(authentication));
    } else { // 否则就使用默认的跳转方式
      super.onAuthenticationSuccess(request,response,authentication);
    }
  }
}

5.自定义登录失败处理逻辑

同登录成功类似,具体代码如下:

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
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;
@Slf4j
@Component
public class MySimpleUrlAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
  @Autowired
  private ObjectMapper objectMapper;

  /**
   * 配置的登录方式
   */
//  @Value("${xxx:默认方式}")
  private String loginType = "JSON";
  @Override
  public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
    log.info("登录失败!!!");

    // 如果配置的登录方式是JSON,就返回json数据
    if ("JSON".equals(loginType)) {
      // 将登录成功的信息写到前端
      response.setStatus(HttpStatus.UNAUTHORIZED.value());
      response.setContentType(MediaType.APPLICATION_JSON_VALUE);
      response.getWriter().write(objectMapper.writeValueAsString(exception));
    } else { // 否则就使用默认的跳转方式,跳转到一个错误页面
      super.onAuthenticationFailure(request,response,exception);
    }
  }
}
@Autowired
  private MySimpleUrlAuthenticationFailureHandler mySimpleUrlAuthenticationFailureHandler;
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
        // 先进controller中去
        .loginPage("/user/auth")
        // 指定自定义登录页面
        .loginPage("/login.html")
        // 登录url
        .loginProcessingUrl("/auth/login")
        .successHandler(myAuthenticationSuccessHandler)
        .failureHandler(mySimpleUrlAuthenticationFailureHandler)
        .and()
        .authorizeRequests()
        // 该controller需要授权
        .antMatchers("/user/auth").permitAll()
        // 添加一个url匹配器,如果匹配到login.html,就授权
        .antMatchers("/login.html").permitAll()
        .anyRequest()
        .authenticated()
        .and()
        // 关闭spring security默认的防csrf攻击
        .csrf().disable();
  }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Java设计模式之中介模式

    Java设计模式之中介模式

    这篇文章主要介绍了Java设计模式之中介模式,中介模式(Mediator Pattern),属于行为型设计模式,目的是把系统中对象之间的调用关系从一对多转变成一对一的调用关系,以此来降低多个对象和类之间的通信复杂性,需要的朋友可以参考下
    2023-12-12
  • 使用AOP的@Around后无返回值的解决

    使用AOP的@Around后无返回值的解决

    这篇文章主要介绍了使用AOP的@Around后无返回值的解决,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Java多线程编程小实例模拟停车场系统

    Java多线程编程小实例模拟停车场系统

    这是一个关于Java多线程编程的例子,用多线程的思想模拟停车场管理系统,这里分享给大家,供需要的朋友参考。
    2017-10-10
  • Java判断用户名和密码是否符合要求过程详解

    Java判断用户名和密码是否符合要求过程详解

    这篇文章主要介绍了Java判断用户名和密码过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • Java C++解决在排序数组中查找数字出现次数问题

    Java C++解决在排序数组中查找数字出现次数问题

    本文终于介绍了分别通过Java和C++实现统计一个数字在排序数组中出现的次数。文中详细介绍了实现思路,感兴趣的小伙伴可以跟随小编学习一下
    2021-12-12
  • elasticsearch集群cluster主要功能详细分析

    elasticsearch集群cluster主要功能详细分析

    这篇文章主要为大家介绍了elasticsearch集群cluster主要功能详细分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-04-04
  • Java线程中synchronized和volatile关键字的区别详解

    Java线程中synchronized和volatile关键字的区别详解

    这篇文章主要介绍了Java线程中synchronized和volatile关键字的区别详解,synchronzied既能够保障可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性,volatile不需要加锁,比synchronized更轻量级,不会阻塞线程,需要的朋友可以参考下
    2024-01-01
  • Java中lambda表达式的基本运用

    Java中lambda表达式的基本运用

    大家好,本篇文章主要讲的是Java中lambda表达式的基本运用,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • Java编程倒计时实现方法示例

    Java编程倒计时实现方法示例

    这篇文章主要介绍了Java编程倒计时实现的三个示例,三种实现方法,具有一定参考价值,需要的朋友可以了解下。
    2017-09-09
  • 使用ObjectMapper解析json不用一直new了

    使用ObjectMapper解析json不用一直new了

    这篇文章主要为大家介绍了使用ObjectMapper解析json不用一直new了的方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06

最新评论