跳至主要內容

Javassm - SpringSecurity5

codejavassmSpring约 2303 字大约 8 分钟

其他配置

在实际开发场景中,我们还会面对各种各样的需求,这一部分我们接着来进行更加深层次的配置。

自定义登录界面

虽然SpringSecurity为我们提供了一个还行的登录界面,但是很多情况下往往都是我们使用自定义的登录界面,这个时候就需要进行更多的配置了

下载好模版后,我们将其中的两个页面和资源文件放到类路径下

注册静态资源

@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}

添加路径匹配

接着我们配置对应页面的Controller控制器:

@Controller
public class HelloController {
    @GetMapping("/")
    public String index(){
        return "index";
    }

    @GetMapping("/login")
    public String login(){
        return "login";
    }
}

这样,我们在登录之后,就可以展示前端模版页面了

不过现在依然是默认进入到SpringSecurity默认的登录界面

自定义登录设置 SecurityFilterChain + formLogin

现在我们来配置自定义的登录界面,将我们的前端模版中的登录页面作为SpringSecurity的默认登录界面。

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
    ...
    //如果你学习过SpringSecurity 5.X版本,可能会发现新版本的配置方式完全不一样
    //新版本全部采用lambda形式进行配置,无法再使用之前的and()方法进行连接了
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                //以下是验证请求拦截和放行配置
                .authorizeHttpRequests(auth -> {
                    auth.anyRequest().authenticated();    
                    //将所有请求全部拦截,一律需要验证
                })
                //以下是表单登录相关配置
                .formLogin(conf -> {
                    conf.loginPage("/login");   
                    //将登录页设置为我们自己的登录页面
                    conf.loginProcessingUrl("/doLogin"); 
                    //登录表单提交的地址,可以自定义
                    conf.defaultSuccessUrl("/");   
                    //登录成功后跳转的页面
                    conf.permitAll();    
                    //将登录相关的地址放行,否则未登录的用户连登录界面都进不去
                    //用户名和密码的表单字段名称,不过默认就是这个,可以不配置,除非有特殊需求
                    conf.usernameParameter("username");
                    conf.passwordParameter("password");
                })
                .build();
    }
}

需要配置登陆页面的地址和登陆请求发送的地址,这里登陆页面填写为/login,登陆请求地址为/doLogin,登陆页面我们刚刚已经自己编写Controller来实现了,登陆请求提交处理由SpringSecurity提供,只需要写路径就可以了。现在访问我们的网站,就可以进入到自定义的登录界面了

静态资源权限处理 requestMatchers

但是我们发现,我们的页面只有一个纯文本,这是因为在获取静态资源的时候,所有的静态资源默认情况下也会被拦截,因此全部被302重定向到登录页面,这显然是不对的

因此,现在我们需要将所有的静态资源也给放行,否则登录界面都没法正常展示:

.authorizeHttpRequests(auth -> {
      auth.requestMatchers("/static/**").permitAll();   
      //将所有的静态资源放行,一定要添加在全部请求拦截之前
      auth.anyRequest().authenticated();    
      //将所有请求全部拦截,一律需要验证
})

再次访问我们的网站,就可以看到正常显示的登录界面了

因此,如果各位小伙伴后续在编写项目过程中发现有302的情况,一定要先检查是否因为没有放行导致被SpringSecurity给拦截了,别再遇到302一脸懵逼了。

前端登录设置

接着我们来配置登录操作,这里我们只需要配置一下登录的地址和登录按钮即可,当然,跟之前一样,要把CSRF的输入框也加上:

<form action="doLogin" method="post">
    ...
  <input type="text" name="username" placeholder="Email Address" class="ad-input">
  ...
  <input type="password" name="password" placeholder="Password" class="ad-input">
  ...
  <input type="text" th:name="${_csrf.getParameterName()}" th:value="${_csrf.token}" hidden>
  <div class="ad-auth-btn">
     <button type="submit" class="ad-btn ad-login-member">Login</button>
  </div>
    ...
</form>

接着我们就可以尝试进行登录操作了。

退出登录操作配置 logout

退出登录也是同样的操作,我们只需要稍微进行一下配置就可以实现,我们首先继续完善配置类:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    ...

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                ...
                //以下是退出登录相关配置
                .logout(conf -> {
                    conf.logoutUrl("/doLogout");   //退出登录地址,跟上面一样可自定义
                    conf.logoutSuccessUrl("/login");  //退出登录成功后跳转的地址,这里设置为登录界面
                    conf.permitAll();
                })
                .build();
    }
}

接着我们来稍微魔改一下页面中的退出登录按钮:

<li>
   <form action="doLogout" method="post">
        <input type="text" th:name="${_csrf.getParameterName()}" th:value="${_csrf.token}" hidden>
        <button type="submit">
           <i class="fas fa-sign-out-alt"></i> logout
        </button>
   </form>
</li>

现在我们点击右上角的退出按钮就可以退出了

取消 csrf 校验

不过,可能会有小伙伴觉得,我们现在无论提交什么请求都需要Csrf校验,有些太麻烦了,实际上现在浏览器已经很安全了,没必要防御到这种程度,我们也可以直接在配置中关闭csrf校验:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    ...
      
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                ...
                //以下是csrf相关配置
                .csrf(conf -> {
                    conf.disable();   //此方法可以直接关闭全部的csrf校验,一步到位
                    conf.ignoringRequestMatchers("/xxx/**");   //此方法可以根据情况忽略某些地址的csrf校验
                })
                .build();
    }
}

这样,我们就不需要再往页面中嵌入CSRF相关的输入框了,发送请求时也不会进行校验,至此,我们就完成了简单的自定义登录界面配置。

记住我功能

我们的网站还有一个重要的功能,就是记住我,也就是说我们可以在登陆之后的一段时间内,无需再次输入账号和密码进行登陆

相当于服务端已经记住当前用户,再次访问时就可以免登陆进入,这是一个非常常用的功能。

我们之前在JavaWeb阶段,使用本地Cookie存储的方式实现了记住我功能,但是这种方式并不安全,同时在代码编写上也比较麻烦,那么能否有一种更加高效的记住我功能实现呢?

rememberMe

SpringSecurity为我们提供了一种优秀的实现,它为每个已经登陆的浏览器分配一个携带Token的Cookie,并且此Cookie默认会被保留14天,只要我们不清理浏览器的Cookie,那么下次携带此Cookie访问服务器将无需登陆,直接继续使用之前登陆的身份,这样显然比我们之前的写法更加简便。

我们需要进行简单配置,即可开启记住我功能:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
    ...
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                ...
                .rememberMe(conf -> {
                    conf.alwaysRemember(false);  
                    //这里不要开启始终记住,我们需要配置为用户自行勾选
                    conf.rememberMeParameter("remember-me");   
                    //记住我表单字段,默认就是这个,可以不配置
                    conf.rememberMeCookieName("xxxx");  
                    //记住我设置的Cookie名字,也可以自定义,不过没必要
                })
                .build();
    }
}

配置完成后,我们需要修改一下前端页面中的表单,将记住我勾选框也作为表单的一部分进行提交:

<div class="ad-checkbox">
    <label>
        <input type="checkbox" name="remember-me" class="ad-checkbox">
        <span>Remember Me</span>
    </label>
</div>

接着我们来尝试勾选记住我选项进行登录:

alt text
alt text

此时提交的表单中就已经包含记住我字段了,我们会发现,服务端返回给我们了一个记住我专属的Cookie信息:

alt text
alt text

这个Cookie信息的过期时间并不是仅会话,而是默认保存一段时间

因此,我们关闭浏览器后下次再次访问网站时,就不需要我们再次进行登录操作了,而是直接继续上一次的登录状态。

当然,由于记住我信息是存放在内存中的,我们需要保证服务器一直处于运行状态

如果关闭服务器的话,记住我信息会全部丢失,因此,如果我们希望记住我能够一直持久化保存,我们就需要进一步进行配置。

PersistentTokenRepository + tokenRespository

我们需要创建一个基于JDBC的TokenRepository实现:

@Bean
public PersistentTokenRepository tokenRepository(DataSource dataSource){
    JdbcTokenRepositoryImpl repository = new JdbcTokenRepositoryImpl();
    //在启动时自动在数据库中创建存储记住我信息的表,仅第一次需要,后续不需要
    repository.setCreateTableOnStartup(true);
    repository.setDataSource(dataSource);
    return repository;
}

然后添加此仓库:

.rememberMe(conf -> {
     conf.rememberMeParameter("remember-me");
     conf.tokenRepository(repository);      
     //设置刚刚的记住我持久化存储库
     conf.tokenValiditySeconds(3600 * 7);   
     //设置记住我有效时间为7天
})

这样,我们就成功配置了数据库持久化存储记住我信息,即使我们重启服务器也不会导致数据丢失。

当我们登录之后,数据库中会自动记录相关的信息:

image-20230704220701000
image-20230704220701000

这样,我们网站的登录系统就更加完善了。

上次编辑于: