Spring security 自定义过滤器实现Json参数传递并兼容表单参数(实例代码)

 更新时间:2021年1月26日 19:46  点击:1834

依赖

 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>

配置安全适配类

基本配置和配置自定义过滤器

package com.study.auth.config.core;
 
import com.study.auth.config.core.authentication.AccountAuthenticationProvider;
import com.study.auth.config.core.authentication.MailAuthenticationProvider;
import com.study.auth.config.core.authentication.PhoneAuthenticationProvider;
import com.study.auth.config.core.filter.CustomerUsernamePasswordAuthenticationFilter;
import com.study.auth.config.core.handler.CustomerAuthenticationFailureHandler;
import com.study.auth.config.core.handler.CustomerAuthenticationSuccessHandler;
import com.study.auth.config.core.handler.CustomerLogoutSuccessHandler;
import com.study.auth.config.core.observer.CustomerUserDetailsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
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.builders.WebSecurity;
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.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 
/**
 * @Package: com.study.auth.config
 * @Description: <>
 * @Author: milla
 * @CreateDate: 2020/09/04 11:27
 * @UpdateUser: milla
 * @UpdateDate: 2020/09/04 11:27
 * @UpdateRemark: <>
 * @Version: 1.0
 */
@Slf4j
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 
  @Autowired
  private AccountAuthenticationProvider provider;
  @Autowired
  private MailAuthenticationProvider mailProvider;
  @Autowired
  private PhoneAuthenticationProvider phoneProvider;
  @Autowired
  private CustomerUserDetailsService userDetailsService;
  @Autowired
  private CustomerAuthenticationSuccessHandler successHandler;
  @Autowired
  private CustomerAuthenticationFailureHandler failureHandler;
  @Autowired
  private CustomerLogoutSuccessHandler logoutSuccessHandler;
 
  /**
   * 配置拦截器保护请求
   *
   * @param http
   * @throws Exception
   */
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    //配置HTTP基本身份验证//使用自定义过滤器-兼容json和表单登录
    http.addFilterBefore(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
        .httpBasic()
        .and().authorizeRequests()
        //表示访问 /setting 这个接口,需要具备 admin 这个角色
        .antMatchers("/setting").hasRole("admin")
        //表示剩余的其他接口,登录之后就能访问
        .anyRequest()
        .authenticated()
        .and()
        .formLogin()
        //定义登录页面,未登录时,访问一个需要登录之后才能访问的接口,会自动跳转到该页面
        .loginPage("/noToken")
        //登录处理接口-登录时候访问的接口地址
        .loginProcessingUrl("/account/login")
        //定义登录时,表单中用户名的 key,默认为 username
        .usernameParameter("username")
        //定义登录时,表单中用户密码的 key,默认为 password
        .passwordParameter("password")
//        //登录成功的处理器
//        .successHandler(successHandler)
//        //登录失败的处理器
//        .failureHandler(failureHandler)
        //允许所有用户访问
        .permitAll()
        .and()
        .logout()
        .logoutUrl("/logout")
        //登出成功的处理
        .logoutSuccessHandler(logoutSuccessHandler)
        .permitAll();
    //关闭csrf跨域攻击防御
    http.csrf().disable();
  }
 
  /**
   * 配置权限认证服务
   *
   * @param auth
   * @throws Exception
   */
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    //权限校验-只要有一个认证通过即认为是通过的(有一个认证通过就跳出认证循环)-适用于多登录方式的系统
//    auth.authenticationProvider(provider);
//    auth.authenticationProvider(mailProvider);
//    auth.authenticationProvider(phoneProvider);
    //直接使用userDetailsService
    auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
  }
 
  /**
   * 配置Spring Security的Filter链
   *
   * @param web
   * @throws Exception
   */
  @Override
  public void configure(WebSecurity web) throws Exception {
    //忽略拦截的接口
    web.ignoring().antMatchers("/noToken");
  }
 
  /**
   * 指定验证manager
   *
   * @return
   * @throws Exception
   */
  @Override
  @Bean
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }
 
 
  /**
   * 注册自定义的UsernamePasswordAuthenticationFilter
   *
   * @return
   * @throws Exception
   */
  @Bean
  public AbstractAuthenticationProcessingFilter customAuthenticationFilter() throws Exception {
    AbstractAuthenticationProcessingFilter filter = new CustomerUsernamePasswordAuthenticationFilter();
    filter.setAuthenticationSuccessHandler(successHandler);
    filter.setAuthenticationFailureHandler(failureHandler);
    //过滤器拦截的url要和登录的url一致,否则不生效
    filter.setFilterProcessesUrl("/account/login");
 
    //这句很关键,重用WebSecurityConfigurerAdapter配置的AuthenticationManager,不然要自己组装AuthenticationManager
    filter.setAuthenticationManager(authenticationManagerBean());
    return filter;
  }
}

自定义过滤器 

根据ContentType是否为json进行判断,如果是就从body中读取参数,进行解析,并生成权限实体,进行权限认证

否则直接使用UsernamePasswordAuthenticationFilter中的方法

package com.study.auth.config.core.filter;
 
import com.fasterxml.jackson.databind.ObjectMapper;
import com.study.auth.config.core.util.AuthenticationStoreUtil;
import com.study.auth.entity.bo.LoginBO;
import lombok.extern.slf4j.Slf4j;
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;
 
/**
 * @Package: com.study.auth.config.core.filter
 * @Description: <>
 * @Author: milla
 * @CreateDate: 2020/09/11 16:04
 * @UpdateUser: milla
 * @UpdateDate: 2020/09/11 16:04
 * @UpdateRemark: <>
 * @Version: 1.0
 */
@Slf4j
public class CustomerUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
 
  /**
   * 空字符串
   */
  private final String EMPTY = "";
 
 
  @Override
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
 
    //如果不是json使用自带的过滤器获取参数
    if (!request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE) && !request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
      String username = this.obtainUsername(request);
      String password = this.obtainPassword(request);
      storeAuthentication(username, password);
      Authentication authentication = super.attemptAuthentication(request, response);
      return authentication;
    }
 
    //如果是json请求使用取参数逻辑
    ObjectMapper mapper = new ObjectMapper();
    UsernamePasswordAuthenticationToken authRequest = null;
    try (InputStream is = request.getInputStream()) {
      LoginBO account = mapper.readValue(is, LoginBO.class);
      storeAuthentication(account.getUsername(), account.getPassword());
      authRequest = new UsernamePasswordAuthenticationToken(account.getUsername(), account.getPassword());
    } catch (IOException e) {
      log.error("验证失败:{}", e);
      authRequest = new UsernamePasswordAuthenticationToken(EMPTY, EMPTY);
    } finally {
      setDetails(request, authRequest);
      Authentication authenticate = this.getAuthenticationManager().authenticate(authRequest);
      return authenticate;
    }
  }
 
  /**
   * 保存用户名和密码
   *
   * @param username 帐号/邮箱/手机号
   * @param password 密码/验证码
   */
  private void storeAuthentication(String username, String password) {
    AuthenticationStoreUtil.setUsername(username);
    AuthenticationStoreUtil.setPassword(password);
  }
}

 其中会有body中的传参问题,所以使用ThreadLocal传递参数

PS:枚举类具备线程安全性

package com.study.auth.config.core.util;
 
/**
 * @Package: com.study.auth.config.core.util
 * @Description: <使用枚举可以保证线程安全>
 * @Author: milla
 * @CreateDate: 2020/09/11 17:48
 * @UpdateUser: milla
 * @UpdateDate: 2020/09/11 17:48
 * @UpdateRemark: <>
 * @Version: 1.0
 */
public enum AuthenticationStoreUtil {
  AUTHENTICATION;
  /**
   * 登录认证之后的token
   */
  private final ThreadLocal<String> tokenStore = new ThreadLocal<>();
  /**
   * 需要验证用户名
   */
  private final ThreadLocal<String> usernameStore = new ThreadLocal<>();
  /**
   * 需要验证的密码
   */
  private final ThreadLocal<String> passwordStore = new ThreadLocal<>();
 
  public static String getUsername() {
    return AUTHENTICATION.usernameStore.get();
  }
 
  public static void setUsername(String username) {
    AUTHENTICATION.usernameStore.set(username);
  }
 
  public static String getPassword() {
    return AUTHENTICATION.passwordStore.get();
  }
 
  public static void setPassword(String password) {
    AUTHENTICATION.passwordStore.set(password);
  }
 
  public static String getToken() {
    return AUTHENTICATION.tokenStore.get();
  }
 
  public static void setToken(String token) {
    AUTHENTICATION.tokenStore.set(token);
  }
 
  public static void clear() {
    AUTHENTICATION.tokenStore.remove();
    AUTHENTICATION.passwordStore.remove();
    AUTHENTICATION.usernameStore.remove();
  }
}

实现UserDetailsService接口

package com.study.auth.config.core.observer;
 
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
 
/**
 * @Package: com.study.auth.config.core
 * @Description: <自定义用户处理类>
 * @Author: milla
 * @CreateDate: 2020/09/04 13:53
 * @UpdateUser: milla
 * @UpdateDate: 2020/09/04 13:53
 * @UpdateRemark: <>
 * @Version: 1.0
 */
@Slf4j
@Component
public class CustomerUserDetailsService implements UserDetailsService {
 
  @Autowired
  private PasswordEncoder passwordEncoder;
 
  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    //测试直接使用固定账户代替
    return User.withUsername("admin").password(passwordEncoder.encode("admin")).roles("admin", "user").build();
  }
}

 登录成功类

package com.study.auth.config.core.handler;
 
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;
 
/**
 * @Package: com.study.auth.config.core.handler
 * @Description: <登录成功处理类>
 * @Author: milla
 * @CreateDate: 2020/09/08 17:39
 * @UpdateUser: milla
 * @UpdateDate: 2020/09/08 17:39
 * @UpdateRemark: <>
 * @Version: 1.0
 */
@Component
public class CustomerAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
  @Override
  public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    HttpServletResponseUtil.loginSuccess(response);
  }
}

 登录失败

package com.study.auth.config.core.handler;
 
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;
 
/**
 * @Package: com.study.auth.config.core.handler
 * @Description: <登录失败操作类>
 * @Author: milla
 * @CreateDate: 2020/09/08 17:42
 * @UpdateUser: milla
 * @UpdateDate: 2020/09/08 17:42
 * @UpdateRemark: <>
 * @Version: 1.0
 */
@Component
public class CustomerAuthenticationFailureHandler implements AuthenticationFailureHandler {
  @Override
  public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
    HttpServletResponseUtil.loginFailure(response, exception);
  }
}

 登出成功类

package com.study.auth.config.core.handler;
 
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
 
/**
 * @Package: com.study.auth.config.core.handler
 * @Description: <登出成功>
 * @Author: milla
 * @CreateDate: 2020/09/08 17:44
 * @UpdateUser: milla
 * @UpdateDate: 2020/09/08 17:44
 * @UpdateRemark: <>
 * @Version: 1.0
 */
@Component
public class CustomerLogoutSuccessHandler implements LogoutSuccessHandler {
  @Override
  public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    HttpServletResponseUtil.logoutSuccess(response);
  }
}

返回值工具类 

package com.study.auth.config.core.handler;
 
import com.alibaba.fastjson.JSON;
import com.study.auth.comm.ResponseData;
import com.study.auth.constant.CommonConstant;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
 
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
 
/**
 * @Package: com.study.auth.config.core.handler
 * @Description: <>
 * @Author: milla
 * @CreateDate: 2020/09/08 17:45
 * @UpdateUser: milla
 * @UpdateDate: 2020/09/08 17:45
 * @UpdateRemark: <>
 * @Version: 1.0
 */
public final class HttpServletResponseUtil {
 
  public static void loginSuccess(HttpServletResponse resp) throws IOException {
    ResponseData success = ResponseData.success();
    success.setMsg("login success");
    response(resp, success);
  }
 
  public static void logoutSuccess(HttpServletResponse resp) throws IOException {
    ResponseData success = ResponseData.success();
    success.setMsg("logout success");
    response(resp, success);
  }
 
  public static void loginFailure(HttpServletResponse resp, AuthenticationException exception) throws IOException {
    ResponseData failure = ResponseData.error(CommonConstant.EX_RUN_TIME_EXCEPTION, exception.getMessage());
    response(resp, failure);
  }
 
  private static void response(HttpServletResponse resp, ResponseData data) throws IOException {
    //直接输出的时候还是需要使用UTF-8字符集
    resp.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
    PrintWriter out = resp.getWriter();
    out.write(JSON.toJSONString(data));
    out.flush();
  }
}

 其他对象见Controller 层返回值的公共包装类-避免每次都包装一次返回-InitializingBean增强

至此,就可以传递Json参数了 

到此这篇关于Spring security 自定义过滤器实现Json参数传递并兼容表单参数的文章就介绍到这了,更多相关Spring security 自定义过滤器内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

[!--infotagslink--]

相关文章

  • Spring AOP 对象内部方法间的嵌套调用方式

    这篇文章主要介绍了Spring AOP 对象内部方法间的嵌套调用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-08-29
  • Spring Cloud 中@FeignClient注解中的contextId属性详解

    这篇文章主要介绍了Spring Cloud 中@FeignClient注解中的contextId属性详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-25
  • gin 获取post请求的json body操作

    这篇文章主要介绍了gin 获取post请求的json body操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-15
  • Springboot如何实现Web系统License授权认证

    这篇文章主要介绍了Springboot如何实现Web系统License授权认证,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-05-28
  • 详解Mysql中的JSON系列操作函数

    新版 Mysql 中加入了对 JSON Document 的支持,可以创建 JSON 类型的字段,并有一套函数支持对JSON的查询、修改等操作,下面就实际体验一下...2016-08-23
  • Json格式详解

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。JSON采用完全独立于语言的文本格式,这些特性使JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成...2021-11-05
  • C#使用Http Post方式传递Json数据字符串调用Web Service

    这篇文章主要为大家详细介绍了C#使用Http Post方式传递Json数据字符串调用Web Service,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
  • 如何在Spring WebFlux的任何地方获取Request对象

    这篇文章主要介绍了如何在Spring WebFlux的任何地方获取Request对象,帮助大家更好的理解和使用springboot框架,感兴趣的朋友可以了解下...2021-01-26
  • 详解SpringCloudGateway内存泄漏问题

    这篇文章主要介绍了详解SpringCloudGateway内存泄漏问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-07-16
  • js遍历json的key和value的实例

    下面小编就为大家带来一篇js遍历json的key和value的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2017-01-26
  • 详解.NET Core 3.0 里新的JSON API

    这篇文章主要介绍了详解.NET Core 3.0 里新的JSON API,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • ASP.NET Core根据环境变量支持多个 appsettings.json配置文件

    这篇文章主要介绍了ASP.NET Core根据环境变量支持多个 appsettings.json配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 基于Vue+Openlayer实现动态加载geojson的方法

    本文通过实例代码给大家介绍基于Vue+Openlayer实现动态加载geojson的方法,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧...2021-09-01
  • Spring为什么不推荐使用@Autowired注解详析

    @Autowired 注解的主要功能就是完成自动注入,使用也非常简单,但这篇文章主要给大家介绍了关于Spring为什么不推荐使用@Autowired注解的相关资料,需要的朋友可以参考下...2021-11-03
  • Springboot如何使用mybatis实现拦截SQL分页

    这篇文章主要介绍了Springboot使用mybatis实现拦截SQL分页,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-06-19
  • jquery对Json的各种遍历方法总结(必看篇)

    下面就为大家带来一篇jquery对Json的各种遍历方法总结(必看篇)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2016-10-02
  • 解决HttpPost+json请求---服务器中文乱码及其他问题

    这篇文章主要介绍了解决HttpPost+json请求---服务器中文乱码及其他问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-22
  • SpringMVC文件上传原理及实现过程解析

    这篇文章主要介绍了SpringMVC文件上传原理及实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-07-15
  • SpringBoot2.x中management.security.enabled=false无效的解决

    这篇文章主要介绍了SpringBoot2.x中management.security.enabled=false无效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-07-23
  • 替换json对象中的key最佳方案

    本文给大家介绍如何替换json对象中的key,通过实例代码给大家介绍key的替换方法,代码也很简单,需要的朋友参考下吧...2021-06-02