文章目录

  • 一、Remember Me
  • 1.1 PersistentTokenRepository
  • 1.2 "记住我"功能配置
  • 二、系统退出控制
  • 三、403处理方案


一、Remember Me

  1. Spring Security中 Remember Me 为“记住我”功能,用户只需要在登录时添加remember-me复选框,取值为true
  2. Spring Security在用户登录后会自动把登录信息存储到数据库,后续就可以不登录进行访问
  3. Spring Security实现Remember Me功能底层实现依赖spring-jdbc,所以需要添加对应依赖
    (如果使用了数据库验证则可以直接无需处理)并配置好与数据库连接相关环境

1.1 PersistentTokenRepository

  1. PersistentTokenRepository是用来管理相应token信息,记住我功能是通过校验第一次登录成功时所分配的token,后续使用token进行校验
  2. PersistentTokenRepository是一个接口,并不能直接实例化
  3. 常见的有以下两个实现类
  • JdbcTokenRepositoryImpl:基于数据库的token存储,推荐使用方法,可以避免重启失效问题
  • InMemoryTokenRepositoryImpl:基于内存的token存储,通常不使用,除非是访问量不大的单体项目,重启服务后token失效

1.2 "记住我"功能配置

  1. 增加RememberMeConfig配置类,用于配置Remember Me相关功能
  • 需要配置token持久化仓库信息bean:PersistentTokenRepository
  1. 在配置类中配置JdbcTokenRepositoryImpl实现类,并指定数据源和对应是否启动时创建表结构
  • org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl
  • 创建表、插入、查询等SQL语句都可以在此类中看到
  • 底层是通过jdbcTemplate进行的操作
  • 指定对应数据源信息和是否需要自动创建表结构
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * 记住我相关配置
 */
@Configuration
public class RememberMeConfig {
    @Resource
    private DataSource dataSource;

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl repository = new JdbcTokenRepositoryImpl();
        // 配置是否启动时初始化表结构,第一次启动时需要,后续注释
        repository.setCreateTableOnStartup(true);
        repository.setDataSource(dataSource);
        return repository;
    }
}
  • 注意自动初始化表结构只能一次,否则将会报错(脚本在后面)
Caused by: java.sql.SQLSyntaxErrorException: Table 'persistent_logins' already exists
  1. 在SecurityConfig中添加RememberMeConfigUserDetailsService实现类对象,并自动注入,同时可以指定token的有效期
  • 也可以指定记住我参数名称,通过**http.rememberMe().rememberMeParameter(“参数名”)**配置
  • 同时也可以指定token验证成功之后的处理器,使用方式和登录成功处理一致
// 记住我配置
http.rememberMe()
    // 指定登录逻辑处理类
    .userDetailsService(userService)
    // 指定失效时间,单位秒
    .tokenValiditySeconds(3600)
    // 指定持久化方式
    .tokenRepository(persistentTokenRepository);
  1. 在登录页面中添加 remember-me 的复选框,只要用户勾选了复选框下次就不需要进行登录了
<!DOCTYPE html>
<html lang="zh">
<head>
     <meta charset="UTF-8">
     <title>系统登录</title>
</head>
<body>
  <form action="/login" method="post">
      用户名:<input type="text" name="username" value="admin" /><br/>
      密码:<input type="password" name="password" value="tianxin" /><br/>
      记住我:<input type="checkbox" name="remember-me" value="true" /><br/>
      <input type="submit" value="登录"/>
  </form>
</body>
</html>
  1. 启动项目,进行正常登录,此时数据库将自动创建对应表结构

springboot绑定机器码 springboot记住登录_springboot绑定机器码

  • 数据库脚本
create table persistent_logins
(
    username  varchar(64)                         not null,
    series    varchar(64)                         not null primary key,
    token     varchar(64)                         not null,
    last_used timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP
)
  1. 重新启动项目,然后正常访问接口,可以看到此时不再要求重新登录
  2. 当进行系统内退出时,Spring Security将会自动删除对应数据库记录
  3. 注意:如果没有退出而进行多次登录数据库中也会进行多次登录记录,退出时会清除所有token记录

二、系统退出控制

  1. 默认Spring Security通过**/logout退出即可,退出后系统会自动跳转到登录页login.html?logout**
  2. 配置信息在org.springframework.security.config.annotation.web.configurers.LogoutConfigurer中

springboot绑定机器码 springboot记住登录_SpringSecurity_02

  1. 可以指定退出的处理路径和页面信息
// 退出配置
http.logout()
    // 退出路径
    .logoutUrl("/logout")
    // 退出成功地址,服务器内页面
    .logoutSuccessUrl("/login.html");
    // 退出成功地址,转到新地址
    //.logoutSuccessUrl("http://www.codecoord.com");
  1. 和登录一样,同样可以指定退出成功后的行为,只需要类实现LogoutSuccessHandler接口,然后在http.logout()中指定logoutSuccessHandler()即可
// 退出配置
http.logout()
    // 退出路径
    .logoutUrl("/logout")
    // 退出成功处理器,和登录处理器一致
    //.logoutSuccessHandler(LogoutSuccessHandler);

三、403处理方案

  1. 当权限不足时将会提示403禁止访问错误,但是对于前后分离的项目,通常通过异步调用,不能直接处理Spring Security返回的错误,所以Spring Security 支持自定义权限受限处理方式

springboot绑定机器码 springboot记住登录_bc_03

  1. AccessDeniedHandler是Spring Security提供用来处理访问被拒接的请求
  • org.springframework.security.web.access.AccessDeniedHandler

springboot绑定机器码 springboot记住登录_spring_04


3. 新建类实现上述接口,用来处理访问拒绝请求,如下面返回json格式错误数据

import com.codecoord.util.JsonUtil;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.Data;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

@Configuration
public class AdvanceAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
            throws IOException, ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        PrintWriter writer = response.getWriter();

        // 返回指定错误信息
        DeniedResponse deniedResponse = new DeniedResponse() {{
            setCode(String.valueOf(HttpStatus.FORBIDDEN.value()));
            setMessage("权限不足,拒绝访问");
            setStatus(false);
        }};
        writer.write(JsonUtil.toJson(deniedResponse));
        writer.flush();
        writer.close();
    }

    @Data
    static class DeniedResponse {
        private boolean status;
        private String code;
        private String message;
    }
}
  1. http里面通过**http.exceptionHandling().accessDeniedHandler()**指定对应处理器
// 指定403处理处理器
http.exceptionHandling().accessDeniedHandler(advanceAccessDeniedHandler);
  1. 启动服务,然后访问没有权限的页面,正常提示返回的信息
{
	"code": "403",
	"message": "权限不足,拒绝访问",
	"status": false
}
  1. 这样可以按照前后端协商的数据格式进行返回,增强了拓展性