文章目录
- 一、Remember Me
- 1.1 PersistentTokenRepository
- 1.2 "记住我"功能配置
- 二、系统退出控制
- 三、403处理方案
一、Remember Me
- Spring Security中 Remember Me 为“记住我”功能,用户只需要在登录时添加remember-me复选框,取值为true
- Spring Security在用户登录后会自动把登录信息存储到数据库,后续就可以不登录进行访问
- Spring Security实现Remember Me功能底层实现依赖spring-jdbc,所以需要添加对应依赖
(如果使用了数据库验证则可以直接无需处理)并配置好与数据库连接相关环境
1.1 PersistentTokenRepository
- PersistentTokenRepository是用来管理相应token信息,记住我功能是通过校验第一次登录成功时所分配的token,后续使用token进行校验
- PersistentTokenRepository是一个接口,并不能直接实例化
- 常见的有以下两个实现类
- JdbcTokenRepositoryImpl:基于数据库的token存储,推荐使用方法,可以避免重启失效问题
- InMemoryTokenRepositoryImpl:基于内存的token存储,通常不使用,除非是访问量不大的单体项目,重启服务后token失效
1.2 "记住我"功能配置
- 增加RememberMeConfig配置类,用于配置Remember Me相关功能
- 需要配置token持久化仓库信息bean:PersistentTokenRepository
- 在配置类中配置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
- 在SecurityConfig中添加RememberMeConfig和UserDetailsService实现类对象,并自动注入,同时可以指定token的有效期
- 也可以指定记住我参数名称,通过**http.rememberMe().rememberMeParameter(“参数名”)**配置
- 同时也可以指定token验证成功之后的处理器,使用方式和登录成功处理一致
// 记住我配置
http.rememberMe()
// 指定登录逻辑处理类
.userDetailsService(userService)
// 指定失效时间,单位秒
.tokenValiditySeconds(3600)
// 指定持久化方式
.tokenRepository(persistentTokenRepository);
- 在登录页面中添加 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>
- 启动项目,进行正常登录,此时数据库将自动创建对应表结构
- 数据库脚本
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
)
- 重新启动项目,然后正常访问接口,可以看到此时不再要求重新登录
- 当进行系统内退出时,Spring Security将会自动删除对应数据库记录
- 注意:如果没有退出而进行多次登录数据库中也会进行多次登录记录,退出时会清除所有token记录
二、系统退出控制
- 默认Spring Security通过**/logout退出即可,退出后系统会自动跳转到登录页login.html?logout**
- 配置信息在org.springframework.security.config.annotation.web.configurers.LogoutConfigurer中
- 可以指定退出的处理路径和页面信息
// 退出配置
http.logout()
// 退出路径
.logoutUrl("/logout")
// 退出成功地址,服务器内页面
.logoutSuccessUrl("/login.html");
// 退出成功地址,转到新地址
//.logoutSuccessUrl("http://www.codecoord.com");
- 和登录一样,同样可以指定退出成功后的行为,只需要类实现LogoutSuccessHandler接口,然后在http.logout()中指定logoutSuccessHandler()即可
// 退出配置
http.logout()
// 退出路径
.logoutUrl("/logout")
// 退出成功处理器,和登录处理器一致
//.logoutSuccessHandler(LogoutSuccessHandler);
三、403处理方案
- 当权限不足时将会提示403禁止访问错误,但是对于前后分离的项目,通常通过异步调用,不能直接处理Spring Security返回的错误,所以Spring Security 支持自定义权限受限处理方式
- AccessDeniedHandler是Spring Security提供用来处理访问被拒接的请求
- org.springframework.security.web.access.AccessDeniedHandler
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;
}
}
- http里面通过**http.exceptionHandling().accessDeniedHandler()**指定对应处理器
// 指定403处理处理器
http.exceptionHandling().accessDeniedHandler(advanceAccessDeniedHandler);
- 启动服务,然后访问没有权限的页面,正常提示返回的信息
{
"code": "403",
"message": "权限不足,拒绝访问",
"status": false
}
- 这样可以按照前后端协商的数据格式进行返回,增强了拓展性