Spring Security表单登录认证

简介: Spring Security是一个强有力并且高度定制化的认证和访问控制框架,致力于为Java应用程序提供认证和授权。

Spring Security是一个强有力并且高度定制化的认证和访问控制框架,致力于为Java应用程序提供认证和授权。

特性:

1、为认证和授权提供综合性和扩展性支持。

2、免受session定位、点击劫持、跨站点请求伪装等攻击。

3、Servelt API集成。

4、与Spring MVC集成

一、Spring Security架构


1、认证

AuthenticationManager是主要的认证策略接口,该接口只包含一个方法。

public interface AuthenticationManager {
  Authentication authenticate(Authentication authentication)
    throws AuthenticationException;
}

认证管理器通过authenticate方法可做下面3处理:

(1)如果能证明输入是一个合理的用户标识,则返回Authentication对象。

(2)如果输入不是一个合理的用户标识,抛出AuthenticationExcepton。

(3)如果无法决策则返回null。

备注:AuthenticationException为运行时异常,根据需要进行处理。

AuthenticationManager常用的实现为ProviderManager,ProviderManager会委托给AuthenticationProvider实例链。

AuthenticationProvider和AuthenticataionManager相似,但提供了额外的方法允许调用者查询是否支持给定的Authentication类型。


public interface AuthenticationProvider {
    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;
    boolean supports(Class<?> authentication);
}

2、授权(访问控制)

认证成功后就是授权,核心策略是AccessDecisionManager,框架提供了访问决策管理器3种实现,它们都会委托给AccessDecisionVoter,就像

ProviderManager委托给AuthenticationProvider。

public interface AccessDecisionVoter {
    boolean supports(ConfigAttribute attribute);
    boolean supports(Class<?> clazz);
    int vote(Authentication authentication, S object,
            Collection<ConfigAttribute> attributes);
}

object参数代表用户想要访问的任何事物(如:web或者java类中的方法),ConfigAttributes代表访问资源所需的许可证等级,

ConfigAttribute是一个接口,只有一个getAttribute()方法,返回值为谁允许访问资源的表达式规则,例如用户角色的名字:ROLE_ADMIN或者ROLE_AUDIT,一般以ROLE_前缀开头。


二、Web Security


1、Spring security在web层是基于Filters的,下面是单个http请求的处理层级:

Client <-> Filter <-> Filter <-> Filter <-> Servlet

客户单发送请求到app,由容器来决定使用哪些filters和servelt,一个请求最多一个servlet来处理,但能有多个filters,多个filters有排序,一个filter能否决其它剩余filter,并且能修改下游filter

和servlet的请求与响应。


Spring security在过滤器链中注册为单个过滤器,具体类型为FilterChainProxy,默认被注册为@Bean来处理所有请求。下面是有过滤器链代理后单个http请求的处理层级:

Client <-> Filter <-> FilterChainProxy(到多个filters) <-> Filter <-> Servlet

备注:事实上在安全过滤器中还有隐含的一层,DelegatingFilterProxy,该代理会委托给FilterChainProxy(在容器中为一个@Bean,并且bean的名字固定为springSecurityFilterChain,该bean包含了所有

安全相关的逻辑)

2、FilterChainProxy下面可以有多个由spring security管理的过滤器链,并且会把请求分发到第一个匹配到的过滤器链,有且只有一个过滤器链来处理。

FilterChainProxy

/foo/**            /bar/**            /**

Filter            Filter            Filter

Filter            Filter            Filter

Filter            Filter            Filter

注意:/foo/**会比/**先匹配到,匹配规则:精确匹配 > 路径匹配 > 通配符匹配 > 缺省匹配


3、一般没有其它自定义的FilterChainProxy有5个或过滤器链,第一个用来忽略静态资源,像/css/**,/images/**以及错误视图/error。最后一个过滤器链匹配/**,

包含认证,授权,异常处理,会话处理,头部写入等等。


三、Java配置


1、表单登录认证

(1)配置类继承自WebSecurityConfigurerAdapter,并带@EnableWebSecurity注解。

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()// 限制基于HttpServletReqeuest的请求访问
          .antMatchers("/", "/home").permitAll()// /和/home路径能被任何人访问
          .anyRequest().authenticated()// 其它请求需要身份认证
          .anyRequest().hasRole("USER")// 其它请求必须是USER角色,该方法默认会加上ROLE_前缀
        .and()
          .formLogin()// 支持基于表单的身份认证
          .loginPage("/login")// 指定跳转到登录页的url,若不指定则会生成默认登录页面,默认为/login
          .loginProcessingUrl("/loginProcess")// 指定认证处理的url,表单action指定地址必须为该地址,默认为/login
          .defaultSuccessUrl("/success")// 认证成功后默认跳转的地址,默认为/home
          .failureUrl("/loginProcess?error")// 认证失败后跳转的地址,默认为/login?error
          .permitAll()// 给用户所有与表单登录相关的url访问授权
        .and()
          .rememberMe()// 开启记住我的功能
          .rememberMeCookieName("remember-me")// 传给浏览器的cookie名,默认为remember-me
          .rememberMeParameter("remember-me")// 前端复选框传入的字段名,默认为remember-me
          .tokenValiditySeconds(30 * 1000)// cookie有效时间
          .key(UUID.randomUUID().toString())// 防止名为remember-me的token被修改的key,默认为随机数,生成随机数需要时间,最好指定固定值
        .and()
          .logout()// 开启退出登录支持
          .logoutUrl("/logout")// 触发退出登录的url,前端页面地址必须为改地址,默认为/logout
          .logoutSuccessUrl("/loginProcess?logout")// 退出登录跳转的地址
          .deleteCookies("remember-me")// 退出登录后删除名为remember-me的cookie,默认会删除remember-me功能对应的cookie
          .permitAll()// 授权所有与退出登录相关的url
        .and()
          .csrf().disable();// 禁用csrf,不禁用则要在表单里加上隐藏域或csrf标签
  }
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // 创建DelegatingPasswordEncoder,该PasswordEncoder会使用BCryptPasswordEncoder对密码进行编码
    PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
    String encodedPwd = encoder.encode("123");
    // 前端传入的密码编码后与加密后的密码比对,编码后的格式为{编码器Id}+编码后的密码
    auth.inMemoryAuthentication().withUser("lyl").password(encodedPwd).roles("USER");
  }
  /**
   * 不重写configure(AuthenticationManagerBuilder auth)方法可以在容器中注册UserDetailService实例
   */
  // @Bean
  // public UserDetailsService userDetailsService() {
  // String encodedPwd = PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("123");
  // UserDetails userDetails = User.withUsername("lyl").password(encodedPwd).roles("USER").build();
  // return new InMemoryUserDetailsManager(userDetails);
  // }
}

(2)Web MVC相关配置

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
  @Override
  public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/success").setViewName("success");
    registry.addViewController("login").setViewName("login");
  }
  @Override
  public void configureViewResolvers(ViewResolverRegistry registry) {
    registry.jsp("/WEB-INF/jsp/", ".jsp");
  }
}
@Controller
public class AuthController {
  @RequestMapping("/loginProcess")
  public String handelRequest(String error, String logout) {
    // 如果是退出登录,则跳转到退出登录后的页面
    if (logout != null) {
      return "logout";
    }
    // 认证、授权失败或者直接访问该地址跳回到登录页面
    return "login";
  }
}

(3)前端jsp表单

<!-- 注意:请求方式必须为post,且action地址必须为loginProcessingUrl配置的地址 -->
<form action="loginProcess" method="post">
  <!-- 如果没禁用csrf则要加上隐藏域,用于传csrf token给后台-->
  <!-- <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> -->
  <table>
    <tr>
      <td>用户名:</td>
      <td><input name="username" type="text" /></td>
    </tr>
    <tr>
      <td>密码:</td>
      <td><input name="password" type="password" /></td>
    </tr>
    <tr>
      <!-- 上传字段名必须和后端Java配置一致,默认为remember-me,用户名和密码也必须与后端配置保持一致,默认为username和password-->
      <td>记住我<input name="remember-me" type="checkbox" checked="checked" /></td>
    </tr>
    <tr>
      <td><input type="submit" value="登录"></td>
    </tr>
  </table>
</form>

2、JDBC Authentication(JDBC认证)

前面的用户名、密码等认证信息是保存在内存中的,实际上用户名、密码都保存在数据库中,Spring Security中提供了JDBC认证,指定数据源和编码器即可,步骤如下:

(1)重写WebSecurityConfigurerAdapter中的configure方法

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   // 创建DelegatingPasswordEncoder,该PasswordEncoder会使用BCryptPasswordEncoder对密码进行编码
   PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
   // 默认数据库模式为HSQL的,因此不要加上withDefaultSchema()
   // 保存在数据库中的密码应该是通过相应密码编码器进行加密的,这里需指定编码器
   auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(encoder);
}

(2)在数据库中创建表,Spring Security默认会从下面表中读取用户身份以及权限信息。


mysql数据库表结构:

create table users(
    username varchar(50) not null primary key,
    password varchar(100) not null,
    enabled boolean not null
);
create table authorities (
    username varchar(50) not null,
    authority varchar(50) not null,
    constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);

oracle数据库表结构:

create table users (
    username varchar2(128) primary key,
    password varchar2(128) not null,
    enabled char(1) check (enabled in ('y','n') ) not null
);
create table authorities (
    username varchar2(128) not null,
    authority varchar2(128) not null
);
alter table authorities add constraint authorities_unique unique(username, authority);
alter table authorities add constraint authorities_fk1 foreign key(username) references users(username) enable;

users表数据如下所示:

1.png

authorities表数据如下所示:

2.png

注意:

(1)users表中的密码必须与配置AuthenticationManager时指定的PasswordEncoder编码后的密码一致。

(2)authorities表中的authority列的值必须以"ROLE_"前缀开头。

3、密码编码器

DelegatingPasswordEncoder(委托密码编码器)



Spring Security5.0以前默认的密码编码器是NoOpPasswordEncoder(要求明文密码),现在默认的密码编码器是BCryptPasswordEncoder。


密码存储格式为:{encoderId}encodedPassword

encoderId为编码器的标识符,用来查找应该使用哪个密码编码器,encoderId必须在编码后的密码之前,并且以"{"开头,以"}"结尾。

下面是不同编码器经过DelegatingPasswordEncoder委托后对原密码"password"进行编码后的密码:

{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG 
{noop}password 
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc 
{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc= 
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0

下面是Spring Security提供的一些PasswordEncoder的实例,与上一 一对应。

BCryptPasswordEncoder
NoOpPasswordEncoder
Pbkdf2PasswordEncoder
SCryptPasswordEncoder
StandardPasswordEncoder
相关文章
|
22天前
|
JSON 安全 Java
什么是JWT?如何使用Spring Boot Security实现它?
什么是JWT?如何使用Spring Boot Security实现它?
81 5
|
2月前
|
安全 Java 数据库
安全无忧!在 Spring Boot 3.3 中轻松实现 TOTP 双因素认证
【10月更文挑战第8天】在现代应用程序开发中,安全性是一个不可忽视的重要环节。随着技术的发展,双因素认证(2FA)已经成为增强应用安全性的重要手段之一。本文将详细介绍如何在 Spring Boot 3.3 中实现基于时间的一次性密码(TOTP)双因素认证,让你的应用安全无忧。
131 5
|
12天前
|
安全 Java 数据安全/隐私保护
基于内存认证的 Spring Security
通过本文的介绍,希望您能够深入理解基于内存认证的Spring Security配置与使用方法,并能够在实际开发中灵活应用这一技术,提升应用的安全性和用户体验。
44 9
|
15天前
|
Java 数据库 数据安全/隐私保护
轻松掌握Spring依赖注入:打造你的登录验证系统
本文以轻松活泼的风格,带领读者走进Spring框架中的依赖注入和登录验证的世界。通过详细的步骤和代码示例,我们从DAO层的创建到Service层的实现,再到Spring配置文件的编写,最后通过测试类验证功能,一步步构建了一个简单的登录验证系统。文章不仅提供了实用的技术指导,还以口语化和生动的语言,让学习变得不再枯燥。
31 2
|
23天前
|
安全 Java 数据安全/隐私保护
如何使用Spring Boot进行表单登录身份验证:从基础到实践
如何使用Spring Boot进行表单登录身份验证:从基础到实践
37 5
|
27天前
|
JSON 安全 算法
Spring Boot 应用如何实现 JWT 认证?
Spring Boot 应用如何实现 JWT 认证?
63 8
|
4月前
|
Java Spring
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
|
4月前
|
Java Spring 安全
Spring 框架邂逅 OAuth2:解锁现代应用安全认证的秘密武器,你准备好迎接变革了吗?
【8月更文挑战第31天】现代化应用的安全性至关重要,OAuth2 作为实现认证和授权的标准协议之一,被广泛采用。Spring 框架通过 Spring Security 提供了强大的 OAuth2 支持,简化了集成过程。本文将通过问答形式详细介绍如何在 Spring 应用中集成 OAuth2,包括 OAuth2 的基本概念、集成步骤及资源服务器保护方法。首先,需要在项目中添加 `spring-security-oauth2-client` 和 `spring-security-oauth2-resource-server` 依赖。
58 0
|
缓存 安全 算法
Spring Security OAuth 2.0 资源服务器— JWT
Spring Security OAuth 2.0 资源服务器— JWT
608 1
|
JSON 安全 Java
Spring Security OAuth 实现 GitHub 快捷登录
Spring Security 5中集成了OAuth的客户端模块,该模块包含以下三个子模块
Spring Security OAuth 实现 GitHub 快捷登录