spring security自定义认证登录的全过程记录

spring security使用分类:

如何使用spring security,相信百度过的都知道,总共有四种用法,从简到深为:

1、不用数据库,全部数据写在配置文件,这个也是官方文档里面的demo;

2、使用数据库,根据spring security默认实现代码设计数据库,也就是说数据库已经固定了,这种方法不灵活,而且那个数据库设计得很简陋,实用性差;

3、spring security和Acegi不同,它不能修改默认filter了,但支持插入filter,所以根据这个,我们可以插入自己的filter来灵活使用;

4、暴力手段,修改源码,前面说的修改默认filter只是修改配置文件以替换filter而已,这种是直接改了里面的源码,但是这种不符合OO设计原则,而且不实际,不可用。

本文主要介绍了关于spring security自定义认证登录的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

1.概要

1.1.简介

spring security是一种基于 Spring AOP 和 Servlet 过滤器的安全框架,以此来管理权限认证等。

1.2.spring security 自定义认证流程

1)认证过程

生成未认证的AuthenticationToken

 ↑(获取信息)  (根据AuthenticationToken分配provider)
 AuthenticationFilter -> AuthenticationManager -> AuthenticationProvider
        ↓(认证)
       UserDetails(一般查询数据库获取)
        ↓(通过)
        生成认证成功的AuthenticationToken
         ↓(存放)
        SecurityContextHolder

2)将AuthenticationFilter加入到security过滤链(资源服务器中配置),如:

http.addFilterBefore(AuthenticationFilter, AbstractPreAuthenticatedProcessingFilter.class)

或者:

http.addFilterAfter(AuthenticationFilter, UsernamePasswordAuthenticationFilter.class)

2.以手机号短信登录为例

2.1.开发环境

  • SpringBoot
  • Spring security
  • Redis

2.2.核心代码分析

2.2.1.自定义登录认证流程

2.2.1.1.自定义认证登录Token

/**
 * 手机登录Token
 *
 * @author : CatalpaFlat
 */
public class MobileLoginAuthenticationToken extends AbstractAuthenticationToken {
 private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
 private static final Logger logger = LoggerFactory.getLogger(MobileLoginAuthenticationToken.class.getName());
 private final Object principal;
 public MobileLoginAuthenticationToken(String mobile) {
 super(null);
 this.principal = mobile;
 this.setAuthenticated(false);
 logger.info("MobileLoginAuthenticationToken setAuthenticated ->false loading ...");
 }
 public MobileLoginAuthenticationToken(Object principal,
      Collection<? extends GrantedAuthority> authorities) {
 super(authorities);
 this.principal = principal;
 // must use super, as we override
 super.setAuthenticated(true);
 logger.info("MobileLoginAuthenticationToken setAuthenticated ->true loading ...");
 }
 @Override
 public void setAuthenticated(boolean authenticated) {
 if (authenticated) {
  throw new IllegalArgumentException(
   "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
 }
 super.setAuthenticated(false);
 }
 @Override
 public Object getCredentials() {
 return null;
 }
 @Override
 public Object getPrincipal() {
 return this.principal;
 }
 @Override
 public void eraseCredentials() {
 super.eraseCredentials();
 }
}

注:

setAuthenticated():判断是否已认证

  • 在过滤器时,会生成一个未认证的AuthenticationToken,此时调用的是自定义token的setAuthenticated(),此时设置为false -> 未认证
  • 在提供者时,会生成一个已认证的AuthenticationToken,此时调用的是父类的setAuthenticated(),此时设置为true -> 已认证

2.2.1.1.自定义认证登录过滤器

/**
 * 手机短信登录过滤器
 *
 * @author : CatalpaFlat
 */
public class MobileLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
 private boolean postOnly = true;
 private static final Logger logger = LoggerFactory.getLogger(MobileLoginAuthenticationFilter.class.getName());
 @Getter
 @Setter
 private String mobileParameterName;
 public MobileLoginAuthenticationFilter(String mobileLoginUrl, String mobileParameterName,
      String httpMethod) {
 super(new AntPathRequestMatcher(mobileLoginUrl, httpMethod));
 this.mobileParameterName = mobileParameterName;
 logger.info("MobileLoginAuthenticationFilter loading ...");
 }
 @Override
 public Authentication attemptAuthentication(HttpServletRequest request,      HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
 if (postOnly && !request.getMethod().equals(HttpMethod.POST.name())) {
  throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
 }
 //get mobile
 String mobile = obtainMobile(request);
 //assemble token
 MobileLoginAuthenticationToken authRequest = new MobileLoginAuthenticationToken(mobile);

 // Allow subclasses to set the "details" property
 setDetails(request, authRequest);

 return this.getAuthenticationManager().authenticate(authRequest);
 }
 /**
 * 设置身份认证的详情信息
 */
 private void setDetails(HttpServletRequest request, MobileLoginAuthenticationToken authRequest) {
 authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
 }
 /**
 * 获取手机号
 */
 private String obtainMobile(HttpServletRequest request) {
 return request.getParameter(mobileParameterName);
 }
 public void setPostOnly(boolean postOnly) {
 this.postOnly = postOnly;
 }
}

注:attemptAuthentication()方法:

  • 过滤指定的url、httpMethod
  • 获取所需请求参数数据封装生成一个未认证的AuthenticationToken
  • 传递给AuthenticationManager认证

2.2.1.1.自定义认证登录提供者

/**
 * 手机短信登录认证提供者
 *
 * @author : CatalpaFlat
 */
public class MobileLoginAuthenticationProvider implements AuthenticationProvider {
 private static final Logger logger = LoggerFactory.getLogger(MobileLoginAuthenticationProvider.class.getName());
 @Getter
 @Setter
 private UserDetailsService customUserDetailsService;
 public MobileLoginAuthenticationProvider() {
 logger.info("MobileLoginAuthenticationProvider loading ...");
 }
 /**
 * 认证
 */
 @Override
 public Authentication authenticate(Authentication authentication) throws AuthenticationException {
 //获取过滤器封装的token信息
 MobileLoginAuthenticationToken authenticationToken = (MobileLoginAuthenticationToken) authentication;
 //获取用户信息(数据库认证)
 UserDetails userDetails = customUserDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal());
 //不通过
 if (userDetails == null) {
  throw new InternalAuthenticationServiceException("Unable to obtain user information");
 }
 //通过
 MobileLoginAuthenticationToken authenticationResult = new MobileLoginAuthenticationToken(userDetails, userDetails.getAuthorities());
 authenticationResult.setDetails(authenticationToken.getDetails());

 return authenticationResult;
 }
 /**
 * 根据token类型,来判断使用哪个Provider
 */
 @Override
 public boolean supports(Class<?> authentication) {
 return MobileLoginAuthenticationToken.class.isAssignableFrom(authentication);
 }
}

注:authenticate()方法

  • 获取过滤器封装的token信息
  • 调取UserDetailsService获取用户信息(数据库认证)->判断通过与否
  • 通过则封装一个新的AuthenticationToken,并返回

2.2.1.1.自定义认证登录认证配置

@Configuration(SpringBeanNameConstant.DEFAULT_CUSTOM_MOBILE_LOGIN_AUTHENTICATION_SECURITY_CONFIG_BN)
public class MobileLoginAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
 private static final Logger logger = LoggerFactory.getLogger(MobileLoginAuthenticationSecurityConfig.class.getName());
 @Value("${login.mobile.url}")
 private String defaultMobileLoginUrl;
 @Value("${login.mobile.parameter}")
 private String defaultMobileLoginParameter;
 @Value("${login.mobile.httpMethod}")
 private String defaultMobileLoginHttpMethod;
 @Autowired
 private CustomYmlConfig customYmlConfig;
 @Autowired
 private UserDetailsService customUserDetailsService;
 @Autowired
 private AuthenticationSuccessHandler customAuthenticationSuccessHandler;
 @Autowired
 private AuthenticationFailureHandler customAuthenticationFailureHandler;
 public MobileLoginAuthenticationSecurityConfig() {
 logger.info("MobileLoginAuthenticationSecurityConfig loading ...");
 }
 @Override
 public void configure(HttpSecurity http) throws Exception {
 MobilePOJO mobile = customYmlConfig.getLogins().getMobile();
 String url = mobile.getUrl();
 String parameter = mobile.getParameter().getMobile();
 String httpMethod = mobile.getHttpMethod();
 MobileLoginAuthenticationFilter mobileLoginAuthenticationFilter = new MobileLoginAuthenticationFilter(StringUtils.isBlank(url) ? defaultMobileLoginUrl : url,
  StringUtils.isBlank(parameter) ? defaultMobileLoginUrl : parameter, StringUtils.isBlank(httpMethod) ? defaultMobileLoginHttpMethod : httpMethod); mobileLoginAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); mobileLoginAuthenticationFilter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler); mobileLoginAuthenticationFilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
 MobileLoginAuthenticationProvider mobileLoginAuthenticationProvider = new MobileLoginAuthenticationProvider(); mobileLoginAuthenticationProvider.setCustomUserDetailsService(customUserDetailsService);
 http.authenticationProvider(mobileLoginAuthenticationProvider)
  .addFilterAfter(mobileLoginAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
 }
}

注:configure()方法

实例化AuthenticationFilter和AuthenticationProvider

将AuthenticationFilter和AuthenticationProvider添加到spring security中。

2.2.2.基于redis自定义验证码校验

2.2.2.1.基于redis自定义验证码过滤器

/**
 * 验证码过滤器
 *
 * @author : CatalpaFlat
 */
@Component(SpringBeanNameConstant.DEFAULT_VALIDATE_CODE_FILTER_BN)
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
 private static final Logger logger = LoggerFactory.getLogger(ValidateCodeFilter.class.getName());
 @Autowired
 private CustomYmlConfig customYmlConfig;
 @Autowired
 private RedisTemplate<Object, Object> redisTemplate;
 /**
  * 验证请求url与配置的url是否匹配的工具类
  */
 private AntPathMatcher pathMatcher = new AntPathMatcher();
 public ValidateCodeFilter() {
  logger.info("Loading ValidateCodeFilter...");
 }
 @Override
 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
         FilterChain filterChain) throws ServletException, IOException {
  String url = customYmlConfig.getLogins().getMobile().getUrl();
  if (pathMatcher.match(url, request.getRequestURI())) {
   String deviceId = request.getHeader("deviceId");
   if (StringUtils.isBlank(deviceId)) {
    throw new CustomException(HttpStatus.NOT_ACCEPTABLE.value(), "Not deviceId in the head of the request");
   }
   String codeParamName = customYmlConfig.getLogins().getMobile().getParameter().getCode();
   String code = request.getParameter(codeParamName);
   if (StringUtils.isBlank(code)) {
    throw new CustomException(HttpStatus.NOT_ACCEPTABLE.value(), "Not code in the parameters of the request");
   }
   String key = SystemConstant.DEFAULT_MOBILE_KEY_PIX + deviceId;
   SmsCodePO smsCodePo = (SmsCodePO) redisTemplate.opsForValue().get(key);
   if (smsCodePo.isExpried()){
    throw new CustomException(HttpStatus.BAD_REQUEST.value(), "The verification code has expired");
   }
   String smsCode = smsCodePo.getCode();
   if (StringUtils.isBlank(smsCode)) {
    throw new CustomException(HttpStatus.BAD_REQUEST.value(), "Verification code does not exist");
   }
   if (StringUtils.equals(code, smsCode)) {
    redisTemplate.delete(key);
    //let it go
    filterChain.doFilter(request, response);
   } else {
    throw new CustomException(HttpStatus.BAD_REQUEST.value(), "Validation code is incorrect");
   }
  }else {
   //let it go
   filterChain.doFilter(request, response);
  }
 }
}

注:doFilterInternal()

自定义验证码过滤校验

2.2.2.2.将自定义验证码过滤器添加到spring security过滤器链

http.addFilterBefore(validateCodeFilter, AbstractPreAuthenticatedProcessingFilter.class)

注:添加到认证预处理过滤器前

3.测试效果

最后附上源码地址:https://gitee.com/CatalpaFlat/springSecurity.git  (本地下载)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • springmvc和js前端的数据传递和接收方式(两种)

    在springmvc中controller的结果集可通过json格式传到js前端接受,也可以通过Map传给前端,具体实现如下 1,通过json格式传递 controller层实现如下 @RequestMapping("queryCityInfo") @ResponseBody public String queryCityInfo()throws Exception{ String provinceId = getString("id"); @SuppressWar

  • 详解Springboot事务管理

    在Spring Boot事务管理中,实现自接口PlatformTransactionManager. public interface PlatformTransactionManager { org.springframework.transaction.TransactionStatus getTransaction(org.springframework.transaction.TransactionDefinition transactionDefinition) throws org.

  • spring mvc实现文件上传与下载功能

    本文实例为大家分享了spring mvc实现文件上传与下载功能的具体代码,供大家参考,具体内容如下 文件上传 在pom.xml中引入spring mvc以及commons-fileupload的相关jar <!-- spring mvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <v

  • Spring自动装配与扫描注解代码详解

    1 javabean的自动装配 自动注入,减少xml文件的配置信息. <?xml version="1.0" encoding="UTF-8"?> <!-- 到入xml文件的约束 --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p&quo

  • SpringBoot 监控管理模块actuator没有权限的问题解决方法

    SpringBoot 1.5.9 版本加入actuator依赖后,访问/beans 等敏感的信息时候报错,如下 Tue Mar 07 21:18:57 GMT+08:00 2017 There was an unexpected error (type=Unauthorized, status=401). Full authentication is required to access this resource. 肯定是权限问题了.有两种方式: 1.关闭权限:application.prop

  • spring Boot打包部署到远程服务器的tomcat中

    前言 Spring Boot项目一般都是内嵌tomcat或者jetty服务器运行,很少用war包部署到外部的服务容器,即使放到linux中,一般也是直接启动Application类,但是有些时候我们需要部署到外部的服务器,这对于Spring Boot来说却有点麻烦 下面话不多说了,来一起看看详细的介绍吧. 环境声明: jdk:1.8 服务器:阿里云,ubuntu 16.04 springBoot:1.5.9.RELEASE 目的 将springBoot 打包到远程服务器的tomcat中. pom

  • Spring MVC参数传递中文乱码解决方法分享

    概述 中国特色社会主义乱码问题是我们经常会碰到的问题,解决的办法有很多,本文分别介绍了GET方式和POST方式中文乱码解决方案中一劳永逸的办法. GET提交中文乱码解决方案 在乱码的Controller文件中采用下面的方法将编码转换成UTF-8 String str = new String(request.getParameter("参数名").getBytes("iso-8859-1"), "utf-8"); 修改项目所在的Tomcat服务器

  • Spring oxm入门实例

    O/XMapper是什么? Spring3.0的一个新特性是O/XMapper.O/X映射器这个概念并不新鲜,O代表Object,X代表XML.它的目的是在Java对象(几乎总是一个plainoldJavaobject,或简写为POJO)和XML文档之间来回转换. 例如,您可能有一个带有几个属性的简单bean,且您的业务需要将那个Java对象转换为一个XML文档.Spring的O/XMapper能够为您解决那个问题.如果反过来,您需要将一个XML文档转换为一个简单Javabean,Spring的

  • spring security自定义认证登录的全过程记录

    spring security使用分类: 如何使用spring security,相信百度过的都知道,总共有四种用法,从简到深为: 1.不用数据库,全部数据写在配置文件,这个也是官方文档里面的demo: 2.使用数据库,根据spring security默认实现代码设计数据库,也就是说数据库已经固定了,这种方法不灵活,而且那个数据库设计得很简陋,实用性差: 3.spring security和Acegi不同,它不能修改默认filter了,但支持插入filter,所以根据这个,我们可以插入自己的f

  • Spring Security自定义认证逻辑实例详解

    目录 前言 分析问题 自定义 Authentication 自定义 Filter 自定义 Provider 自定义认证成功/失败后的 Handler 配置自定义认证的逻辑 测试 总结 前言 这篇文章的内容基于对Spring Security 认证流程的理解,如果你不了解,可以读一下这篇文章:Spring Security 认证流程 . 分析问题 以下是 Spring Security 内置的用户名/密码认证的流程图,我们可以从这里入手: 根据上图,我们可以照猫画虎,自定义一个认证流程,比如手机短

  • Spring Security自定义认证器的实现代码

    目录 Authentication AuthenticationProvider SecurityConfigurerAdapter UserDetailsService TokenFilter 登录过程 在了解过Security的认证器后,如果想自定义登陆,只要实现AuthenticationProvider还有对应的Authentication就可以了 Authentication 首先要创建一个自定义的Authentication,Security提供了一个Authentication的子

  • SpringBoot security安全认证登录的实现方法

    目录 前言 一.登录时序图 二.配置与代码 1.引入库 2.代码文件 参考文档 前言 本文章主要从spring security安全认证登录内部调用流程来流程分析登录过程. 一.登录时序图 时序原图 二.配置与代码 1.引入库 pom.xml: <!-- Spring框架基本的核心工具 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-contex

  • Spring Security 自定义短信登录认证的实现

    自定义登录filter 上篇文章我们说到,对于用户的登录,security通过定义一个filter拦截login路径来实现的,所以我们要实现自定义登录,需要自己定义一个filter,继承AbstractAuthenticationProcessingFilter,从request中提取到手机号和验证码,然后提交给AuthenticationManager: public class SmsAuthenticationFilter extends AbstractAuthenticationPro

  • Spring Security自定义登录页面认证过程常用配置

    目录 一.自定义登录页面 1.编写登录页面 2.修改配置类 3.编写控制器 二. 认证过程其他常用配置 1.失败跳转 1.1编写页面 1.2修改表单配置 1.3添加控制器方法 1.4设置fail.html不需要认证 2.设置请求账户和密码的参数名 2.1源码简介 2.2修改配置 2.3修改页面 3.自定义登录成功处理器 3.1源码分析 3.2代码实现 4.自定义登录失败处理器 4.1源码分析 4.2代码实现 一.自定义登录页面 虽然Spring Security给我们提供了登录页面,但是对于实际

  • Spring Security自定义登录原理及实现详解

    1. 前言 前面的关于 Spring Security 相关的文章只是一个预热.为了接下来更好的实战,如果你错过了请从 Spring Security 实战系列 开始.安全访问的第一步就是认证(Authentication),认证的第一步就是登录.今天我们要通过对 Spring Security 的自定义,来设计一个可扩展,可伸缩的 form 登录功能. 2. form 登录的流程 下面是 form 登录的基本流程: 只要是 form 登录基本都能转化为上面的流程.接下来我们看看 Spring

  • Spring Security使用数据库登录认证授权

    目录 一.搭建项目环境 1.创建 RBAC五张表 2.创建项目 二.整合 Spring Security实现用户认证 1.后端整合 2.前端整合 三.整合 Spring Security实现用户授权 1.后端 2.前端 一.搭建项目环境 1.创建 RBAC五张表 RBAC,即基于角色的权限访问控制(Role-Based Access Control),就是用户通过角色与权限进行关联.在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系. 在 MySQL数据库中,创建如下几个表: D

  • spring security自定义登录页面

    在项目中我们肯定不能使用Spring自己生成的登录页面,而要用我们自己的登录页面,下面讲一下如何自定义登录页面,先看下配置 <sec:http auto-config="true"> <sec:intercept-url pattern="/app.jsp" access="ROLE_SERVICE"/> <sec:intercept-url pattern="/**" access="

  • 详解Spring Security的formLogin登录认证模式

    一.formLogin的应用场景 在本专栏之前的文章中,已经给大家介绍过Spring Security的HttpBasic模式,该模式比较简单,只是进行了通过携带Http的Header进行简单的登录验证,而且没有定制的登录页面,所以使用场景比较窄. 对于一个完整的应用系统,与登录验证相关的页面都是高度定制化的,非常美观而且提供多种登录方式.这就需要Spring Security支持我们自己定制登录页面,也就是本文给大家介绍的formLogin模式登录认证模式. 准备工作 新建一个Spring B

随机推荐