SpringSecurity基于散列加密方案实现自动登录

下面是详细讲解基于散列加密方案实现 Spring Security 自动登录的攻略。

1. 简介

Spring Security 是一个基于 Spring 框架实现的安全框架,它提供了一系列的安全服务,在 Web 安全、认证、授权等方面有着非常好的表现。其中之一就是实现自动登录。

自动登录是指用户在第一次登录之后,下一次再进入系统时,无需再次输入账号和密码,直接进入已登录状态。Spring Security 提供了记住我功能,可以通过 Cookie 或 Token 的方式,实现自动登录。

在 Spring Security 中,记住我功能默认是基于 Token 实现。Token 通常是一个随机生成的字符串,保存在 Cookie 或 Redis 等缓存中,并与用户信息建立映射关系。在用户下一次访问 System 系统时,会携带 Token,以完成自动登录。

本文将详细讲解如何使用 Spring Security 基于散列加密方案实现自动登录。

2. 散列加密方案

在实现自动登录之前,我们需要先讲解散列加密方案。散列是指将任意长度的消息压缩到固定长度的算法,常见的散列算法包括 MD5、SHA1、SHA256 等。这些算法具有不可逆性和抗碰撞性,通常用于密码加密和数据完整性校验等方面。

在 Spring Security 中,它提供了 BCryptPasswordEncoder 和 ShaPasswordEncoder 两种散列加密器实现。

BCryptPasswordEncoder:这是 Spring Security 推荐的一种散列加密器,它支持动态加盐和可自定义强度等特性,函数开销可调节。比较安全,因此比较慢。

ShaPasswordEncoder:这是一种底层使用 SHA 算法的加密器,不支持加盐和强度调节等特性,速度较快,但相对不够安全。

3. 记住我功能实现流程

下面是记住我功能实现的流程:

  1. 用户输入账号和密码后,勾选记住我选项,并提交表单;
  2. Spring Security 会自动识别用户是否勾选了记住我选项,如果勾选,会在登录成功后,生成一个 Token,并将 Token 保存在 Cookie 或 Redis 等缓存中,并与用户信息建立映射关系;
  3. 用户下一次访问时,Spring Security 会自动解析 Cookie 或缓存中的 Token,查询映射关系,如果查询成功,则直接登录成功,否则继续要求用户输入账号和密码。

4. 实现步骤

以下是基于散列加密方案实现自动登录的步骤:

4.1 配置 Spring Security

首先,需要添加 Spring Security 依赖,并在配置中指定加密器和用户相关信息。

  1. 添加 Spring Security 依赖
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>5.5.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.5.1</version>
</dependency>
  1. 配置 Spring Security
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
                .antMatchers("/index").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/login")
                .successHandler(new LoginSuccessHandler())
                .failureHandler(new LoginFailureHandler())
                .permitAll()
            .and()
            .rememberMe()
                .userDetailsService(userDetailsService)
                .rememberMeParameter("remember-me")
                .key("spring-security-demo")
                .tokenValiditySeconds(60 * 60 * 24 * 7) // 7 天
            .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(new LogoutSuccessHandler())
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder);
    }
}
  • @EnableWebSecurity:启用 Spring Security;
  • configure(HttpSecurity http):配置 HTTP 安全策略,包括访问权限和认证方式等;
  • configure(AuthenticationManagerBuilder auth):进行身份认证的用户来源和密码处理方式;

在上面的代码中,我们配置了散列加密器为 BCryptPasswordEncoder,并且将 UserDetailsService 实现类和加密器注入到了 SecurityConfig 中。同时,通过配置 permitAll(),将 /login/logout/index 等 URL 放开,其他请求都需要进行身份认证才能访问。

4.2 执行自动登录

为了执行自动登录,我们需要在登录成功后,生成 Token 并将 Token 保存在 Cookie 中。代码实现可以在 SecurityConfig 中添加扩展点 LoginSuccessHandler 的方式实现。

public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Autowired
    private TokenRepository tokenRepository;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {

        // 生成 Token
        Token token = new Token();
        token.setUserId(((User)authentication.getPrincipal()).getId());
        token.setToken(UUID.randomUUID().toString());
        token.setExpireTime(LocalDateTime.now().plusDays(7)); // 7 天有效期

        // 保存 Token
        token = tokenRepository.save(token);

        // 将 Token 保存到 Cookie 中
        Cookie cookie = new Cookie("remember-me", token.getToken());
        cookie.setMaxAge(60 * 60 * 24 * 7); // 7 天有效期
        cookie.setPath("/");
        response.addCookie(cookie);

        super.onAuthenticationSuccess(request, response, authentication);
    }
}

在上述代码中,我们生成一个 Token 对象,并设置 Token 的有效期和用户 ID 等。然后将 Token 保存到数据库中,并将 Token 保存在 Cookie 中,设置 Cookie 的有效期与 Token 的有效期一致,路径为 "/",表示在整个站点内都可以访问这个 Cookie。

4.3 自动登录逻辑

当用户下一次访问站点时,需要进行自动登录。Spring Security 会自动识别 Cookie 中是否包含 remember-me 的值,如果存在,则会根据值查询 Token,并根据 Token 进行自动登录。

具体实现如下:

  1. 添加 Redis 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 添加 TokenRepository
public interface TokenRepository extends JpaRepository<Token, Long> {

    Token findByToken(String token);
}
  1. Spring Security 中配置 Redis
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // ...

    @Bean
    public RedisOperationsSessionRepository sessionRepository() {
        RedisConnectionFactory connectionFactory = redisConnectionFactory();
        return new RedisOperationsSessionRepository(redisTemplate(connectionFactory));
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new RedisConnectionFactoryBuilder()
                .redisConfiguration(RedisConfiguration.defaultConfiguration())
                .build();
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new JdkSerializationRedisSerializer());
        return template;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
                .antMatchers("/index").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/login")
                .successHandler(new LoginSuccessHandler())
                .failureHandler(new LoginFailureHandler())
                .permitAll()
            .and()
            .rememberMe()
                .userDetailsService(userDetailsService)
                .rememberMeParameter("remember-me")
                .key("spring-security-demo")
                .tokenValiditySeconds(60 * 60 * 24 * 7) // 7 天
                .tokenRepository(tokenRepository())
            .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(new LogoutSuccessHandler())
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .permitAll();
    }

    @Bean
    public TokenRepository tokenRepository() {
        return new TokenRepository() {
            @Override
            public Token findByToken(String token) {
                HttpSession session = SecurityContextUtils.get()
                        .getAuthentication().getDetails();
                return (Token) session.getAttribute(token);
            }

            @Override
            public <S extends Token> S save(S entity) {
                HttpSession session = SecurityContextUtils.get()
                        .getAuthentication().getDetails();
                session.setAttribute(entity.getToken(), entity);
                return entity;
            }
        };
    }
}

在上述代码中,我们添加了 Redis 依赖,并且在配置中注册了 RedisTemplate、RedisConnectionFactory 和 RedisOperationsSessionRepository 等 Bean,用于执行 Redis 相关的逻辑。

configure(HttpSecurity http) 中,我们增加了 rememberMe().tokenRepository(tokenRepository());同时,实现了一个内存 TokenRepository 的版本,如下代码所示:

@Bean
public TokenRepository tokenRepository() {
    return new TokenRepository() {
        private final Map<String, Token> map = new ConcurrentHashMap<>();

        @Override
        public Token findByToken(String token) {
            return map.get(token);
        }

        @Override
        public <S extends Token> S save(S entity) {
            map.put(entity.getToken(), entity);
            return entity;
        }
    };
}

对于 Token 的存储,我们使用了 Redis,甚至可以选择使用数据库,而上边的实现则是在内存中保存。

这样,当用户再次访问时,Spring Security 就可以通过查询 TokenRepository 来验证 Token 的正确性,并获取对应的用户信息完成自动登录的流程。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringSecurity基于散列加密方案实现自动登录 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Sping Security前后端分离两种实战方案

    下面我将详细讲解“Spring Security前后端分离两种实战方案”的完整攻略。 简介 Spring Security 是一款开源的认证和授权框架,可以实现对系统的安全性保护。在前后端分离的架构中,Spring Security 的实现需要特别注意。本文将从两个方面介绍 Spring Security 的前后端分离实现方案。 方案一:基于 Token 的…

    Java 2023年6月3日
    00
  • SpringBoot RESTful 应用中的异常处理梳理小结

    SpringBoot RESTful 应用中的异常处理是非常重要的一部分。异常处理可以让我们及时的判断和处理错误,保障服务的稳定性和可靠性。在这里,我将为您提供关于 SpringBoot RESTful 应用中异常处理的梳理和完整攻略。 一、异常处理的基本架构 异常处理的基本架构分为两个部分:异常拦截器和异常处理器。拦截器负责拦截异常,而处理器则负责对异常进…

    Java 2023年5月27日
    00
  • C# 中Excel导入时判断是否被占用三种方法

    下面是详细讲解 “C# 中 Excel 导入时判断是否被占用三种方法” 的完整攻略。 一、需求说明 在使用 C# 程序导入 Excel 数据时,可能会遇到一个问题,即当 Excel 文件正在被其他程序占用时,程序无法正确读取数据。因此我们需要通过一些方法判断 Excel 文件是否被其他程序占用。 二、方法一 第一种方法是通过 try…catch 来判断 …

    Java 2023年5月19日
    00
  • java求数组最大值和最小数示例分享

    Java求数组最大值和最小值示例分享 在Java开发中,我们经常需要对数组中的元素进行操作。其中,求出数组的最大值和最小值是常见操作之一。下面我们将会介绍两种不同的方法来求数组的最大值和最小值。 方法一: 遍历比较法 遍历比较法是一种简单粗暴的方法。我们可以通过循环遍历数组中的每一个元素,并且在遍历的过程中与当前的最大值或最小值进行比较。当我们遍历完整个数组…

    Java 2023年5月26日
    00
  • 把textarea中字符串里含有的回车换行替换成<br>的javascript代码

    将textarea中字符串里含有的回车换行替换成<br>的javascript代码可以通过正则表达式以及字符串操作来实现,具体步骤如下: 第一步:获取textarea中的值 我们可以通过JavaScript来获取textarea中的值,代码示例如下: const textArea = document.querySelector(‘textare…

    Java 2023年6月15日
    00
  • Java代码读取properties配置文件

    读取properties配置文件 package com.easycrud.utils; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; import java.util.Map; import java.util.Properties; i…

    Java 2023年5月2日
    00
  • Java中的IO流是什么?

    Java中的IO流是一种机制,用于与存储在计算机硬盘或网络上的数据进行交互。I/O是输入和输出的缩写,实际上涵盖了多种数据传输方向,其中包括读入数据(输入)和写出数据(输出)到其他地方。在Java中,输入和输出统称为流。 Java中的IO流用于将数据从源读取到目的地,数据源和目的地可以是文件、socket、内存中的缓存等等。可以使用标准的输入和输出流Syst…

    Java 2023年4月27日
    00
  • Spring MVC整合Shiro权限控制的方法

    下面是“Spring MVC整合Shiro权限控制的方法”的完整攻略。 一、简介 Shiro是一个开源的安全框架,可以提供认证、授权、加密和会话管理等安全相关功能。Spring MVC是一个流行的Web框架,提供了建立Web应用程序的开发模型和程序依赖管理。本文将介绍如何在Spring MVC中整合Shiro权限控制。 二、整合步骤 1. 引入依赖 首先,在…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部