– 在项目中,如果要覆盖默认权限、授权自动配置,需要让 DefaultWebSecurityCondition 这个类失效
– 添加如下配置可以实现自定义对资源权限规则设置
package com.vinjcent.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* 重写 WebSecurityConfigurerAdapter 类使得默认 DefaultWebSecurityCondition 条件失效
*/
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/index").permitAll() // 放行资源
.anyRequest().authenticated() // 【注意】所有放行的资源放在任何拦截请求前面
.and() // 链式编程,继续拼接
.formLogin();
}
}
# 说明
- permitAll() 代表放行该资源,该资源为公共资源,无需认证和授权可以直接访问
- anyRequest().authenticated() 代表所有请求必须认证之后才能访问
- formLogin() 代表开启表单认证
# 【注意】放行资源必须放在所有认证请求之前!
根据前面分析可知,校验用户名密码是根据 UsernamePasswordAuthenticationFilter 这个过滤器执行的,要求
修改默认的登录配置信息
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
dependencies>
package com.vinjcent.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
}
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户登录title>
head>
<body>
<h1>用户登录h1>
<form th:action="@{/login}" method="post">
用户名: <input type="text" name="uname"> <br>
密码: <input type="password" name="passwd"> <br>
<input type="submit" value="登录">
form>
body>
html>
【注意】当前login.html页面的文本输入框name参数以及请求路径可根据自定义修改,但必须与 WebSecurityConfiguration 的配置一致
package com.vinjcent.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* 重写 WebSecurityConfigurerAdapter 类使得默认 DefaultWebSecurityCondition 条件失效
*/
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/toLogin").permitAll() // 放行登录页面
.mvcMatchers("/index").permitAll() // 放行资源
.anyRequest().authenticated() // 【注意】所有放行的资源放在任何拦截请求前面
.and() // 链式编程,继续拼接
.formLogin()
.loginPage("/toLogin") // 指定登录页面,【注意】一旦指定登陆页面之后,必须指定登录请求url
.loginProcessingUrl("/login") // 指定处理登录请求的url
.usernameParameter("uname") // 指定认证用户名参数的名称
.passwordParameter("passwd") // 指定认证密码参数的名称
// .successForwardUrl("/hello") // 认证成功后跳转的路径(转发),始终在认证成功之后跳转到指定路径
// .defaultSuccessUrl("/index", true) // 认证成功之后的跳转(重定向),如果在访问权限资源之前被拦截,那么认证之后将会跳转之前先访问的资源
.and()
.csrf().disable(); // 禁止 csrf 跨站请求保护
}
}
有时候页面跳转并不能满足我们,特别是在前后端分离开发中就不需要成功之后跳转页面(不是交由后端去做,而是前端去做)。此时,我们只需要给前端返回一个 JSON 通知登录成功还是失败。这个时候可以通过自定义接口
successHandler 认证成功处理
该处理函数中带有一个 AuthenticationSuccessHandler 接口参数
自定义 AuthenticationSuccessHandler 实现类
package com.vinjcent.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义认证成功之后处理
*/
public class DivAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map<String, Object> result = new HashMap<>();
result.put("msg","登陆成功");
result.put("status", 200);
result.put("authentication", authentication);
response.setContentType("application/json;charset=UTF-8");
String info = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(info);
}
}
package com.vinjcent.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* 重写 WebSecurityConfigurerAdapter 类使得默认 DefaultWebSecurityCondition 条件失效
*/
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/toLogin")
.loginProcessingUrl("/login")
.usernameParameter("uname")
.passwordParameter("passwd")
// .successForwardUrl("/hello")
// .defaultSuccessUrl("/index", true)
.successHandler(new DivAuthenticationSuccessHandler()) // 认证成功时处理,前后端分离解决方案
.and()
.csrf().disable();
}
}
为了能更直观在登录页面看到异常错误信息,可以在登录页面中直接获取异常信息。Spring Security 在登陆失败之后会将异常信息存储到 request
、session
作用域中,key 为 SPRING_SECURITY_LAST_EXCEPTION
命名属性
验证失败信息
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户登录title>
head>
<body>
<h2>
h2>
<h1>用户登录h1>
<form th:action="@{/login}" method="post">
用户名: <input type="text" name="uname"> <br>
密码: <input type="password" name="passwd"> <br>
<input type="submit" value="登录">
form>
body>
html>
package com.vinjcent.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* 重写 WebSecurityConfigurerAdapter 类使得默认 DefaultWebSecurityCondition 条件失效
*/
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/toLogin")
.loginProcessingUrl("/login")
.usernameParameter("uname")
.passwordParameter("passwd")
// .successForwardUrl("/hello")
// .defaultSuccessUrl("/index", true)
.successHandler(new DivAuthenticationSuccessHandler())
// .failureForwardUrl("/toLogin") // 认证失败后跳转的路径(转发)
// .failureUrl("/toLogin") // 认证失败后跳转的路径(重定向)
.and()
.csrf().disable();
}
}
failureHandler 认证失败处理
该处理函数中带有一个 AuthenticationFailureHandler 接口参数
自定义 DivAuthenticationFailureHandler 实现类
package com.vinjcent.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义认证失败之后处理
*/
public class DivAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
Map<String, Object> result = new HashMap<>();
result.put("msg", "登陆失败: " + exception.getMessage());
result.put("status", 500);
response.setContentType("application/json;charset=UTF-8");
String info = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(info);
}
}
package com.vinjcent.config;
import com.vinjcent.handler.DivAuthenticationFailureHandler;
import com.vinjcent.handler.DivAuthenticationSuccessHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* 重写 WebSecurityConfigurerAdapter 类使得默认 DefaultWebSecurityCondition 条件失效
*/
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/toLogin")
.loginProcessingUrl("/login")
.usernameParameter("uname")
.passwordParameter("passwd")
// .successForwardUrl("/hello")
// .defaultSuccessUrl("/index", true)
.successHandler(new DivAuthenticationSuccessHandler()) // 认证成功时处理,前后端分离解决方案
// .failureForwardUrl("/toLogin")
// .failureUrl("/toLogin")
.failureHandler(new DivAuthenticationFailureHandler()) // 认证失败时处理,前后端分离解决方案
.and()
.csrf().disable();
}
}
SpringSecurity 中也提供了默认的注销登录(访问**/logout**),在开发时可以按照自己的需求对注销进行个性化定制
默认开启
)package com.vinjcent.config;
import com.vinjcent.handler.DivAuthenticationFailureHandler;
import com.vinjcent.handler.DivAuthenticationSuccessHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* 重写 WebSecurityConfigurerAdapter 类使得默认 DefaultWebSecurityCondition 条件失效
*/
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/toLogin")
.loginProcessingUrl("/login")
.usernameParameter("uname")
.passwordParameter("passwd")
// .successForwardUrl("/hello")
// .defaultSuccessUrl("/index", true)
.successHandler(new DivAuthenticationSuccessHandler())
// .failureForwardUrl("/toLogin")
// .failureUrl("/toLogin")
.failureHandler(new DivAuthenticationFailureHandler())
.and()
.logout() // 注销登录的logout
.logoutUrl("/logout") // 指定注销的路径url 【注意】请求方式类型必须是GET
.invalidateHttpSession(true) // 默认开启,会话清除
.clearAuthentication(true) // 默认开启,清除认证标记
.logoutSuccessUrl("/toLogin") // 注销登录成功之后跳转的页面
.and()
.csrf().disable();
}
}
logout
package com.vinjcent.config;
import com.vinjcent.handler.DivAuthenticationFailureHandler;
import com.vinjcent.handler.DivAuthenticationSuccessHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* 重写 WebSecurityConfigurerAdapter 类使得默认 DefaultWebSecurityCondition 条件失效
*/
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/toLogin")
.loginProcessingUrl("/login")
.usernameParameter("uname")
.passwordParameter("passwd")
// .successForwardUrl("/hello")
// .defaultSuccessUrl("/index", true)
.successHandler(new DivAuthenticationSuccessHandler())
// .failureForwardUrl("/toLogin")
// .failureUrl("/toLogin")
.failureHandler(new DivAuthenticationFailureHandler())
.and()
.logout()
// .logoutUrl("/logout")
.logoutRequestMatcher(new OrRequestMatcher( // 配置多个注销登录的请求
new AntPathRequestMatcher("/aLogout", "GET"),
new AntPathRequestMatcher("/bLogout", "POST")
))
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/toLogin")
.and()
.csrf().disable();
}
}
/logout
接口package com.vinjcent.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
@RequestMapping("/toLogout")
public String toLogout() {
return "logout";
}
}
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>注销登录title>
head>
<body>
<h1>注销登录h1>
<form th:action="@{/bLogout}" method="post">
<input type="submit" value="注销登录">
form>
body>
html>
如果是前后端分离开发,注销成功后就不需要页面跳转了,只需要将注销成功的信息返回前端即可,此时可以通过自定义 LogoutSuccessHandler 实现类返回内容注销之后信息
logoutSuccessHandler 认证失败处理
该处理函数中带有一个 LogoutSuccessHandler 接口参数
自定义 DivLogoutSuccessHandler 实现类
package com.vinjcent.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义注销成功之后处理
*/
public class DivLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map<String, Object> result = new HashMap<>();
result.put("msg","注销成功,当前认证对象为:" + authentication);
result.put("status", 200);
response.setContentType("application/json;charset=UTF-8");
String info = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(info);
}
}
package com.vinjcent.config;
import com.vinjcent.handler.DivAuthenticationFailureHandler;
import com.vinjcent.handler.DivAuthenticationSuccessHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* 重写 WebSecurityConfigurerAdapter 类使得默认 DefaultWebSecurityCondition 条件失效
*/
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/toLogin")
.loginProcessingUrl("/login")
.usernameParameter("uname")
.passwordParameter("passwd")
// .successForwardUrl("/hello")
// .defaultSuccessUrl("/index", true)
.successHandler(new DivAuthenticationSuccessHandler())
// .failureForwardUrl("/toLogin")
// .failureUrl("/toLogin")
.failureHandler(new DivAuthenticationFailureHandler())
.and()
.logout()
// .logoutUrl("/logout")
// .logoutRequestMatcher(new OrRequestMatcher(
// new AntPathRequestMatcher("/aLogout", "GET"),
// new AntPathRequestMatcher("/bLogout", "POST")
//))
.logoutSuccessHandler(new DivLogoutSuccessHandler()) // 成功退出登录时,前后端分离解决方案
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/toLogin")
.and()
.csrf().disable();
}
}
当前完整所有Security配置信息
package com.vinjcent.config;
import com.vinjcent.handler.DivAuthenticationFailureHandler;
import com.vinjcent.handler.DivAuthenticationSuccessHandler;
import com.vinjcent.handler.DivLogoutSuccessHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
/**
* 重写 WebSecurityConfigurerAdapter 类使得默认 DefaultWebSecurityCondition 条件失效
*/
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/toLogin").permitAll() // 放行登录页面
.mvcMatchers("/index").permitAll() // 放行资源
.anyRequest().authenticated() // 【注意】所有放行的资源放在任何拦截请求前面
.and() // 链式编程,继续拼接
.formLogin()
.loginPage("/toLogin") // 指定登录页面,【注意】一旦指定登陆页面之后,必须指定登录请求url
.loginProcessingUrl("/login") // 指定处理登录请求的url
.usernameParameter("uname") // 指定认证用户名参数的名称
.passwordParameter("passwd") // 指定认证密码参数的名称
// .successForwardUrl("/hello") // 认证成功后跳转的路径(转发),始终在认证成功之后跳转到指定路径
// .defaultSuccessUrl("/index", true) // 认证成功之后的跳转(重定向),如果在访问权限资源之前被拦截,那么认证之后将会跳转之前先访问的资源
.successHandler(new DivAuthenticationSuccessHandler()) // 认证成功时处理,前后端分离解决方案
// .failureForwardUrl("/toLogin") // 认证失败后跳转的路径(转发)
// .failureUrl("/toLogin") // 认证失败后跳转的路径(重定向)
.failureHandler(new DivAuthenticationFailureHandler()) // 认证失败时处理,前后端分离解决方案
.and()
.logout() // 注销登录的logout
// .logoutUrl("/logout") // 指定注销的路径url 【注意】请求方式类型必须是GET
//.logoutRequestMatcher(new OrRequestMatcher( // 配置多个注销登录的请求
// new AntPathRequestMatcher("/aLogout", "GET"),
// new AntPathRequestMatcher("/bLogout", "POST")
//))
.logoutSuccessHandler(new DivLogoutSuccessHandler()) // 成功退出登录时,前后端分离解决方案
.invalidateHttpSession(true) // 默认开启,会话清除
.clearAuthentication(true) // 默认开启,清除认证标记
.logoutSuccessUrl("/toLogin") // 注销登录成功之后跳转的页面
.and()
.csrf().disable(); // 禁止 csrf 跨站请求保护
}
}
SecurityContextHolder
SecurityContextHolder 用来获取登录之后用户信息。SpringSecurity 会将登录用户数据保存在Session
中。但是,为了使用方便, SpringSecurity 在此基础上还做了一些改进,其中最主要的一个变化就是线程绑定。当用户登录成功后,SpringSecurity 会将登录成功的用户信息保存到 SecurityContextHolder 中。
SecurityContextHolder 中的数据保存默认是通过ThreadLocal
来实现的,使用 ThreadLocal 创建的变量只能被当前线程访问,不能被其他线程访问和修改,也就是用户数据和请求线程绑定在一起。当登录请求处理完毕后, SpringSecurity 会将 SecurityContextHolder 中的数据拿出来保存到 Session 中,同时将 SecurityContexHolder 中的数据清空。以后每当有请求到来时,SpringSecurity 就会先从Session
中取出用户登录数据,保存到 SecurityContextHolder 中,方便在该请求的后续处理过程中使用,同时在请求结束时将 SecurityContextHolder 中的数据拿出来保存到 Session 中,然后将 Security 中的 SecurityContextHolder 中的数据清空。这一策略非常方便用户在Controller、Service层以及任何代码中获取当前登录用户数据
这种设计是典型的策略设计模式
public class SecurityContextHolder {
public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
public static final String MODE_GLOBAL = "MODE_GLOBAL";
private static final String MODE_PRE_INITIALIZED = "MODE_PRE_INITIALIZED";
private static String strategyName = System.getProperty(SYSTEM_PROPERTY);
private static SecurityContextHolderStrategy strategy;
// ...
private static void initializeStrategy() {
if (MODE_PRE_INITIALIZED.equals(strategyName)) {
Assert.state(strategy != null, "When using " + MODE_PRE_INITIALIZED
+ ", setContextHolderStrategy must be called with the fully constructed strategy");
return;
}
if (!StringUtils.hasText(strategyName)) {
// Set default
strategyName = MODE_THREADLOCAL;
}
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
return;
}
if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
return;
}
if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
return;
}
// Try to load a custom strategy
try {
Class<?> clazz = Class.forName(strategyName);
Constructor<?> customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
// ...
}
MODE THREADLOCAL
:这种存放策略是将 SecurityContext 存放在 ThreadLocal 中,由于 Threadlocal 的特点是在哪个线程中存储就要在哪个线程中读取,这其实非常适合 web 应用,因为在默认情况下,一个请求无论经过多少 Filter 到达 Servlet,都是由一个线程来处理的。这也是 SecurityContextHolder 的默认存储策略,这种存储策略意味着如果在具体的业务处理代码中,开启了子线程,在子线程中去获取登录用户数据,就会获取不到MODE INHERITABLETHREADLOCAL
:这种存储模式适用于多线程环境,如果希望在子线程中也能够获取到登录用户数据,那么可以使用这种存储模式MODE GLOBAL
:这种存储模式实际上是将数据保存在一个静态变量中,在 JavaWeb 开发中,这种模式很少使用到SecurityContextHolderStrategy
通过 SecurityContextHolderStrategy 得知, SecurityContextHolderStrategy 接口用来定义存储策略方法
接口中一共定义了四个方法:
从上面可以看到,每一个实现类对应一种策略的实现
application.yml
文件# 端口号
server:
port: 3035
# 服务应用名称
spring:
application:
name: SpringSecurity02
# 关闭thymeleaf缓存(用于修改完之后立即生效)
thymeleaf:
cache: false
# 配置登录用户名、密码
security:
user:
name: root
password: root
roles:
- admin
- user
package com.vinjcent.controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
System.out.println("Hello Security");
// 1.获取认证信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User user = (User) authentication.getPrincipal();
System.out.println("身份信息: " + user.getUsername());
System.out.println("权限信息: " + user.getAuthorities());
// 模拟子线程获取(默认策略为本地线程,不支持多线程获取)
new Thread(() -> {
Authentication authentication1 = SecurityContextHolder.getContext().getAuthentication();
System.out.println("子线程获取: " + authentication1);
}).start();
return "Hello Security";
}
}
单线程情况下
多线程情况下
# 根据下图 SYSTEM_PROPERTY 系统属性配置
-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL
# MODE_THREADLOCAL
# MODE_INHERITABLETHREADLOCAL
# MODE_GLOBAL
# MODE_PRE_INITIALIZED
SYSTEM_PROPERTY
加载的,因此我们可以通过增加 VM Options 参数进行修改
<dependency>
<groupId>org.thymeleaf.extrasgroupId>
<artifactId>thymeleaf-extras-springsecurity5artifactId>
<version>3.0.4.RELEASEversion>
dependency>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<ul>
<li sec:authentication="principal.username">li>
<li sec:authentication="principal.authorities">li>
<li sec:authentication="principal.accountNonExpired">li>
<li sec:authentication="principal.accountNonLocked">li>
<li sec:authentication="principal.credentialsNonExpired">li>
ul>