Spring Security在标准登录表单中添加一个额外的字段

概述

在本文中,我们将通过向标准登录表单添加额外字段来实现Spring Security的自定义身份验证方案。

我们将重点关注两种不同的方法,以展示框架的多功能性以及我们可以使用它的灵活方式。

我们的第一种方法是一个简单的解决方案,专注于重用现有的核心Spring Security实现。

我们的第二种方法是更加定制的解决方案,可能更适合高级用例。

2. Maven设置

我们将使用Spring Boot启动程序来引导我们的项目并引入所有必需的依赖项。
 我们将使用的设置需要父声明,Web启动器和安全启动器;我们还将包括thymeleaf :

<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.0.0.M7</version>
 <relativePath/>
</parent>

<dependencies>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
  </dependency>
  <dependency>
  <groupId>org.thymeleaf.extras</groupId>
  <artifactId>thymeleaf-extras-springsecurity4</artifactId>
 </dependency>
</dependencies>

可以在Maven Central找到最新版本的Spring Boot安全启动器。

3.简单的项目设置

在我们的第一种方法中,我们将专注于重用Spring Security提供的实现。特别是,我们将重用DaoAuthenticationProvider和UsernamePasswordToken,因为它们是“开箱即用”的。

关键组件包括:

•SimpleAuthenticationFilter - UsernamePasswordAuthenticationFilter的扩展
•SimpleUserDetailsService - UserDetailsService的实现
•User - Spring Security提供的User类的扩展,它声明了我们的额外域字段
•SecurityConfig - 我们的Spring Security配置,它将SimpleAuthenticationFilter插入到过滤器链中,声明安全规则并连接依赖项
•login.html - 收集用户名,密码和域的登录页面

3.1. 简单Authentication Filter

在我们的SimpleAuthenticationFilter中,域和用户名字段是从请求中提取的。我们连接这些值并使用它们来创建UsernamePasswordAuthenticationToken的实例。

然后将令牌传递给AuthenticationProvider进行身份验证:

public class SimpleAuthenticationFilter
 extends UsernamePasswordAuthenticationFilter {
 @Override
 public Authentication attemptAuthentication(
  HttpServletRequest request,
  HttpServletResponse response)
  throws AuthenticationException {
  // ...
  UsernamePasswordAuthenticationToken authRequest
   = getAuthRequest(request);
  setDetails(request, authRequest);
  return this.getAuthenticationManager()
   .authenticate(authRequest);
 }
 private UsernamePasswordAuthenticationToken getAuthRequest(
  HttpServletRequest request) {
  String username = obtainUsername(request);
  String password = obtainPassword(request);
  String domain = obtainDomain(request);
  // ...
  String usernameDomain = String.format("%s%s%s", username.trim(),
   String.valueOf(Character.LINE_SEPARATOR), domain);
  return new UsernamePasswordAuthenticationToken(
   usernameDomain, password);
 }
 // other methods
}

3.2.简单的UserDetails服务

UserDetailsService定义了一个名为loadUserByUsername的方法。我们的实现提取用户名和域名。然后将值传递给我们的UserRepository以获取用户:

public class SimpleUserDetailsService implements UserDetailsService {
 // ...
 @Override
 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  String[] usernameAndDomain = StringUtils.split(
   username, String.valueOf(Character.LINE_SEPARATOR));
  if (usernameAndDomain == null || usernameAndDomain.length != 2) {
   throw new UsernameNotFoundException("Username and domain must be provided");
  }
  User user = userRepository.findUser(usernameAndDomain[0], usernameAndDomain[1]);
  if (user == null) {
   throw new UsernameNotFoundException(
    String.format("Username not found for domain, username=%s, domain=%s",
    usernameAndDomain[0], usernameAndDomain[1]));
  }
  return user;
 }
}

3.3. Spring Security配置

我们的设置与标准的Spring Security配置不同,因为我们在默认情况下通过调用addFilterBefore将SimpleAuthenticationFilter插入到过滤器链中:

@Override
protected void configure(HttpSecurity http) throws Exception {

 http
  .addFilterBefore(authenticationFilter(),
  UsernamePasswordAuthenticationFilter.class)
  .authorizeRequests()
  .antMatchers("/css/**", "/index").permitAll()
  .antMatchers("/user/**").authenticated()
  .and()
  .formLogin().loginPage("/login")
  .and()
  .logout()
  .logoutUrl("/logout");
}

我们可以使用提供的DaoAuthenticationProvider,因为我们使用SimpleUserDetailsService配置它。回想一下,我们的SimpleUserDetailsService知道如何解析我们的用户名和域字段,并返回在验证时使用的相应用户。

public AuthenticationProvider authProvider() {
 DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
 provider.setUserDetailsService(userDetailsService);
 provider.setPasswordEncoder(passwordEncoder());
 return provider;
}

由于我们使用的是SimpleAuthenticationFilter,因此我们配置自己的AuthenticationFailureHandler以确保正确处理失败的登录尝试:

public SimpleAuthenticationFilter authenticationFilter() throws Exception {
 SimpleAuthenticationFilter filter = new SimpleAuthenticationFilter();
 filter.setAuthenticationManager(authenticationManagerBean());
 filter.setAuthenticationFailureHandler(failureHandler());
 return filter;
}

3.4.登录页面

我们使用的登录页面收集我们的SimpleAuthenticationFilter提取的额外的字段:

<form class="form-signin" th:action="@{/login}" method="post">
 <h2 class="form-signin-heading">Please sign in</h2>
 <p>Example: user / domain / password</p>
 <p th:if="${param.error}" class="error">Invalid user, password, or domain</p>
 <p>
 <label for="username" class="sr-only">Username</label>
 <input type="text" id="username" name="username" class="form-control"
  placeholder="Username" required autofocus/>
 </p>
 <p>
 <label for="domain" class="sr-only">Domain</label>
 <input type="text" id="domain" name="domain" class="form-control"
  placeholder="Domain" required autofocus/>
 </p>
 <p>
 <label for="password" class="sr-only">Password</label>
 <input type="password" id="password" name="password" class="form-control"
  placeholder="Password" required autofocus/>
 </p>
 <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button><br/>
 <p><a href="/index" rel="external nofollow" th:href="@{/index}" rel="external nofollow" >Back to home page</a></p>
</form>

当我们运行应用程序并访问http:// localhost:8081上下文时,我们会看到一个访问安全页面的链接。单击该链接将显示登录页面。正如所料,我们看到了额外的域名字段

image

3.5.总结

在我们的第一个例子中,我们能够通过“伪造”用户名字段来重用DaoAuthenticationProvider和UsernamePasswordAuthenticationToken。

因此,我们能够使用最少量的配置和其他代码添加对额外登录字段的支持。

4.自定义项目设置

我们的第二种方法与第一种方法非常相似,但可能更适合于非平凡用例。

我们的第二种方法的关键组成部分包括:

•CustomAuthenticationFilter - UsernamePasswordAuthenticationFilter的扩展
•CustomUserDetailsService - 声明loadUserbyUsernameAndDomain方法的自定义接口
•CustomUserDetailsServiceImpl - CustomUserDetailsService的实现
•CustomUserDetailsAuthenticationProvider - AbstractUserDetailsAuthenticationProvider的扩展
•CustomAuthenticationToken - UsernamePasswordAuthenticationToken的扩展
•User - Spring Security提供的User类的扩展,它声明了我们的额外域字段
•SecurityConfig - 我们的Spring Security配置,它将CustomAuthenticationFilter插入到过滤器链中,声明安全规则并连接依赖项
•login.html - 收集用户名,密码和域的登录页面

4.1.自定义验证过滤器

在我们的CustomAuthenticationFilter中,我们从请求中提取用户名,密码和域字段。这些值用于创建CustomAuthenticationToken的实例,该实例将传递给AuthenticationProvider进行身份验证:

public class CustomAuthenticationFilter
 extends UsernamePasswordAuthenticationFilter {
 public static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "domain";
 @Override
 public Authentication attemptAuthentication(
  HttpServletRequest request,
  HttpServletResponse response)
   throws AuthenticationException {
  // ...
  CustomAuthenticationToken authRequest = getAuthRequest(request);
  setDetails(request, authRequest);
  return this.getAuthenticationManager().authenticate(authRequest);
 }
 private CustomAuthenticationToken getAuthRequest(HttpServletRequest request) {
  String username = obtainUsername(request);
  String password = obtainPassword(request);
  String domain = obtainDomain(request);
  // ...
  return new CustomAuthenticationToken(username, password, domain);
 }

4.2.自定义UserDetails服务

我们的CustomUserDetailsService合约定义了一个名为loadUserByUsernameAndDomain的方法。

我们创建的CustomUserDetailsServiceImpl类只是实现并委托我们的CustomUserRepository来获取用户:

public UserDetails loadUserByUsernameAndDomain(String username, String domain)
 throws UsernameNotFoundException {
 if (StringUtils.isAnyBlank(username, domain)) {
  throw new UsernameNotFoundException("Username and domain must be provided");
 }
 User user = userRepository.findUser(username, domain);
 if (user == null) {
  throw new UsernameNotFoundException(
   String.format("Username not found for domain, username=%s, domain=%s",
   username, domain));
 }
 return user;
}

4.3.自定义UserDetailsAuthenticationProvider

我们的CustomUserDetailsAuthenticationProvider将AbstractUserDetailsAuthenticationProvider和委托扩展到我们的CustomUserDetailService以检索用户。这个类最重要的特性是retrieveUser方法的实现。

请注意,我们必须将身份验证令牌强制转换为CustomAuthenticationToken才能访问我们的自定义字段:

@Override
protected UserDetails retrieveUser(String username,
 UsernamePasswordAuthenticationToken authentication)
 throws AuthenticationException {
 CustomAuthenticationToken auth = (CustomAuthenticationToken) authentication;
 UserDetails loadedUser;
 try {
  loadedUser = this.userDetailsService
   .loadUserByUsernameAndDomain(auth.getPrincipal()
   .toString(), auth.getDomain());
 } catch (UsernameNotFoundException notFound) {
  if (authentication.getCredentials() != null) {
   String presentedPassword = authentication.getCredentials()
    .toString();
   passwordEncoder.matches(presentedPassword, userNotFoundEncodedPassword);
  }
  throw notFound;
 } catch (Exception repositoryProblem) {
  throw new InternalAuthenticationServiceException(
   repositoryProblem.getMessage(), repositoryProblem);
 }
 // ...
 return loadedUser;
}

4.4.总结

我们的第二种方法几乎与我们首先提出的简单方法相同。通过实现我们自己的AuthenticationProvider和CustomAuthenticationToken,我们避免了需要使用自定义解析逻辑来调整我们的用户名字段。

5.结论

在本文中,我们在Spring Security中实现了一个使用额外登录字段的表单登录。我们以两种不同的方式做到了这一点
•在我们简单的方法中,我们最小化了我们需要编写的代码量。通过使用自定义解析逻辑调整用户名,我们能够重用DaoAuthenticationProvider和UsernamePasswordAuthentication
•在我们更加个性化的方法中,我们通过扩展AbstractUserDetailsAuthenticationProvider并使用CustomAuthenticationToken提供我们自己的CustomUserDetailsService来提供自定义字段支持。

与往常一样,所有源代码都可以在GitHub上找到

总结

以上所述是小编给大家介绍的Spring Security在标准登录表单中添加一个额外的字段,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 详解使用Spring Security OAuth 实现OAuth 2.0 授权

    OAuth 2.0 是一种工业级的授权协议.OAuth 2.0是从创建于2006年的OAuth 1.0继承而来的.OAuth 2.0致力于帮助开发者简化授权并为web应用.桌面应用.移动应用.嵌入式应用提供具体的授权流程. OAuth 2.0 is the industry-standard protocol for authorization. OAuth 2.0 supersedes the work done on the original OAuth protocol created i

  • SpringBoot结合SpringSecurity实现图形验证码功能

    本文介绍了SpringBoot结合SpringSecurity实现图形验证码功能,分享给大家,具体如下: 生成图形验证码 根据随机数生成图片 将随机数存到Session中 将生成的图片写到接口的响应中 生成图形验证码的过程比较简单,和SpringSecurity也没有什么关系.所以就直接贴出代码了 根据随机数生成图片 /** * 生成图形验证码 * @param request * @return */ private ImageCode generate(ServletWebRequest r

  • 详解如何在spring boot中使用spring security防止CSRF攻击

    CSRF是什么? CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF.  CSRF可以做什么? 你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求.CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全. CSRF漏洞现状 CSRF这种攻击方式

  • Spring Security结合JWT的方法教程

    概述 众所周知使用 JWT 做权限验证,相比 Session 的优点是,Session 需要占用大量服务器内存,并且在多服务器时就会涉及到共享 Session 问题,在手机等移动端访问时比较麻烦 而 JWT 无需存储在服务器,不占用服务器资源(也就是无状态的),用户在登录后拿到 Token 后,访问需要权限的请求时附上 Token(一般设置在Http请求头),JWT 不存在多服务器共享的问题,也没有手机移动端访问问题,若为了提高安全,可将 Token 与用户的 IP 地址绑定起来 前端流程 用户

  • SpringBoot+SpringSecurity处理Ajax登录请求问题(推荐)

    最近在项目中遇到了这样一个问题:前后端分离,前端用Vue来做,所有的数据请求都使用vue-resource,没有使用表单,因此数据交互都是使用JSON,后台使用Spring Boot,权限验证使用了Spring Security,因为之前用Spring Security都是处理页面的,这次单纯处理Ajax请求,因此记录下遇到的一些问题.这里的解决方案不仅适用于Ajax请求,也可以解决移动端请求验证. 创建工程 首先我们需要创建一个Spring Boot工程,创建时需要引入Web.Spring S

  • 详解spring security之httpSecurity使用示例

    httpSecurity 类似于spring security的xml配置文件命名空间配置中的<http>元素.它允许对特定的http请求基于安全考虑进行配置.默认情况下,适用于所有的请求,但可以使用requestMatcher(RequestMatcher)或者其它相似的方法进行限制. 使用示例: 最基本的基于表单的配置如下.该配置将所有的url访问权限设定为角色名称为"ROLE_USER".同时也定义了内存认证模式:使用用户名"user"和密码&qu

  • SpringBoot + Spring Security 基本使用及个性化登录配置详解

    Spring Security 基本介绍 这里就不对Spring Security进行过多的介绍了,具体的可以参考官方文档 我就只说下SpringSecurity核心功能: 认证(你是谁) 授权(你能干什么) 攻击防护(防止伪造身份) 基本环境搭建 这里我们以SpringBoot作为项目的基本框架,我这里使用的是maven的方式来进行的包管理,所以这里先给出集成Spring Security的方式 <dependencies> ... <dependency> <groupI

  • spring security 5.x实现兼容多种密码的加密方式

    前言 本文主要给大家介绍了关于spring security 5.x实现兼容多种密码的加密方式,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1.spring security PasswordEncoder spring security 5不需要配置密码的加密方式,而是用户密码加前缀的方式表明加密方式,如: {MD5}88e2d8cd1e92fd5544c8621508cd706b代表使用的是MD5加密方式: {bcrypt}$2a$10$eZeGvVV2ZXr/vgiV

  • Spring Security在标准登录表单中添加一个额外的字段

    概述 在本文中,我们将通过向标准登录表单添加额外字段来实现Spring Security的自定义身份验证方案. 我们将重点关注两种不同的方法,以展示框架的多功能性以及我们可以使用它的灵活方式. 我们的第一种方法是一个简单的解决方案,专注于重用现有的核心Spring Security实现. 我们的第二种方法是更加定制的解决方案,可能更适合高级用例. 2. Maven设置 我们将使用Spring Boot启动程序来引导我们的项目并引入所有必需的依赖项.  我们将使用的设置需要父声明,Web启动器和安

  • Spring Security配置多个数据源并添加登录验证码的实例代码

    目录 1.配置多个数据源 2. 添加登录验证码 1.配置多个数据源 多个数据源是指在同一个系统中,用户数据来自不同的表,在认证时,如果第一张表没有查找到用户,那就去第二张表中査询,依次类推. 看了前面的分析,要实现这个需求就很容易了,认证要经过AuthenticationProvider,每一 个 AuthenticationProvider 中都配置了一个 UserDetailsService,而不同的 UserDetailsService 则可以代表不同的数据源,所以我们只需要手动配置多个A

  • Spring Security实现统一登录与权限控制的示例代码

    目录 项目介绍 统一认证中心 配置授权服务器 配置WebSecurity 登录 菜单 鉴权 资源访问的一些配置 有用的文档 项目介绍 最开始是一个单体应用,所有功能模块都写在一个项目里,后来觉得项目越来越大,于是决定把一些功能拆分出去,形成一个一个独立的微服务,于是就有个问题了,登录.退出.权限控制这些东西怎么办呢?总不能每个服务都复制一套吧,最好的方式是将认证与鉴权也单独抽离出来作为公共的服务,业务系统只专心做业务接口开发即可,完全不用理会权限这些与之不相关的东西了.于是,便有了下面的架构图:

  • Spring Security实现验证码登录功能

    这篇文章主要介绍了Spring Security实现验证码登录功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在spring security实现登录注销功能的基础上进行开发. 1.添加生成验证码的控制器. (1).生成验证码 /** * 引入 Security 配置属性类 */ @Autowired private SecurityProperties securityProperties; @Override public ImageC

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

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

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

  • Spring Security使用单点登录的权限功能

    目录 背景 Spring Security 实现 已经有了单点登录页面,Spring Security怎么登录,不登录可以拿到权限吗 Authentication 继续分析 结论 如何手动登录Spring Security 结论 总结 附 参考 背景 在配置中心增加权限功能 目前配置中心已经包含了单点登录功能,可以通过统一页面进行登录,登录完会将用户写入用户表 RBAC的用户.角色.权限表CRUD.授权等都已经完成 希望不用用户再次登录,就可以使用SpringSecurity的权限控制 Spri

  • Spring Security实现退出登录和退出处理器

    在系统中一般都有退出登录的操作.退出登录后,Spring Security进行了以下操作: 清除认证状态 销毁HttpSession对象 跳转到登录页面 配置退出登录的路径和退出后跳转的路径 //退出登录配置 http.logout() .logoutUrl("/logout") .logoutSuccessUrl("/login.html") .clearAuthentication(true) .invalidateHttpSession(true); 在网页中

  • Spring Security OAuth2 实现登录互踢的示例代码

    本文主要介绍了Spring Security OAuth2 实现登录互踢的示例代码,分享给大家,具体如下: 背景说明 一个账号只能一处登录,类似的业务需求在现有后管类系统是非常常见的. 但在原有的 spring security oauth2 令牌方法流程(所谓的登录)无法满足类似的需求. 我们先来看 TokenEndpoint 的方法流程 客户端 带参访问 /oauth/token 接口,最后去调用 TokenGranter TokenGranter 根据不同的授权类型,获取用户认证信息 并去

随机推荐