SpringBoot整合SpringSecurity系列(5)-登录状态控制

一、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
  2. 在配置类中配置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. 启动项目,进行正常登录,此时数据库将自动创建对应表结构

在这里插入图片描述

  • 数据库脚本
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中

在这里插入图片描述

  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 支持自定义权限受限处理方式

在这里插入图片描述

  1. 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;
    }
}

  1. http里面通过**http.exceptionHandling().accessDeniedHandler()**指定对应处理器
// 指定403处理处理器
http.exceptionHandling().accessDeniedHandler(advanceAccessDeniedHandler);
  1. 启动服务,然后访问没有权限的页面,正常提示返回的信息
{
	"code": "403",
	"message": "权限不足,拒绝访问",
	"status": false
}
  1. 这样可以按照前后端协商的数据格式进行返回,增强了拓展性
1. 引入依赖 在pom.xml文件中引入Spring Security依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 2. 添加配置类 在项目中添加一个配置类SecurityConfig,继承自WebSecurityConfigurerAdapter。 ``` @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/login", "/register").permitAll() // 登录注册页面不需要验证 .anyRequest().authenticated() // 其他页面需要验证 .and() .formLogin() .loginPage("/login") // 登录页面 .defaultSuccessUrl("/index") // 登录成功后的默认跳转页面 .and() .logout() .logoutUrl("/logout") // 退出登录的URL .logoutSuccessUrl("/login") // 退出登录后跳转到的页面 .invalidateHttpSession(true) .deleteCookies("JSESSIONID"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() // 在内存中存储用户信息 .withUser("admin").password("{noop}admin").roles("ADMIN") .and() .withUser("user").password("{noop}user").roles("USER"); } } ``` 3. 实现登录与注册功能 创建登录页面login.html和注册页面register.html,使用Thymeleaf模板引擎渲染页面。 在Controller中添加登录和注册的请求处理方法。 ``` @Controller public class UserController { @GetMapping("/login") public String login() { return "login"; } @PostMapping("/login") public String loginSuccess() { return "redirect:/index"; } @GetMapping("/register") public String register() { return "register"; } @PostMapping("/register") public String registerSuccess() { return "redirect:/login"; } } ``` 4. 实现用户鉴权功能 在SecurityConfig中重写configure(AuthenticationManagerBuilder auth)方法,实现用户信息的认证。 这里使用inMemoryAuthentication()方法在内存中存储用户信息,实际应用中可以使用数据库存储。 ``` @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() // 在内存中存储用户信息 .withUser("admin").password("{noop}admin").roles("ADMIN") .and() .withUser("user").password("{noop}user").roles("USER"); } ``` 在Controller中添加需要鉴权的请求处理方法,并在方法上添加@PreAuthorize注解指定需要的权限。 ``` @Controller public class UserController { @GetMapping("/admin") @PreAuthorize("hasRole('ADMIN')") public String admin() { return "admin"; } @GetMapping("/user") @PreAuthorize("hasRole('USER')") public String user() { return "user"; } } ``` 5. 配置登录认证 在SecurityConfig中重写configure(HttpSecurity http)方法,配置登录认证信息。 ``` @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/login", "/register").permitAll() // 登录注册页面不需要验证 .anyRequest().authenticated() // 其他页面需要验证 .and() .formLogin() .loginPage("/login") // 登录页面 .defaultSuccessUrl("/index") // 登录成功后的默认跳转页面 .and() .logout() .logoutUrl("/logout") // 退出登录的URL .logoutSuccessUrl("/login") // 退出登录后跳转到的页面 .invalidateHttpSession(true) .deleteCookies("JSESSIONID"); } ``` 6. 测试 启动应用,在浏览器中访问http://localhost:8080/login,输入用户名和密码进行登录登录成功后跳转到首页。 访问http://localhost:8080/admin和http://localhost:8080/user,根据用户角色的不同,页面会有不同的显示。如果访问没有权限的页面,会自动跳转到登录页面。在登录状态下访问/logout可以退出登录
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值