OAuth2协议 白话理解+SpringCloud Security 栗子
- 引言
- 白话理解专业名词
- 认证授权服务和资源服务
- scope作用域
- 关于白话理解
- 搭建认证授权服务
- 引入依赖
- 编写认证配置(SecurityConfig)
- 配置授权服务
- 基于内存Token的授权服务配置
- 基于JwtToken的授权服务配置
- 申请token
- 简化模式申请token
- 申请授权码
- 根据授权码申请token
- 密码模式申请token
- 刷新token
- 检查token
- 搭建资源服务
- 引入依赖
- 配置资源服务
- 远程调用认证授权服务的TokenService资源验证
- 基于JWT的无状态资源验证
- 自定义验证失败处理
- 资源服务中如何获取用户信息
引言
OAuth2.0
是OAuth
协议的延续版本
,但不向前兼容OAuth 1.0(即完全废止了OAuth1.0)。 OAuth 2.0
关注客户端开发者的简易性
。要么通过组织在资源拥有者和HTTP服务商之间的被批准的交互动作代表用户,要么允许第三方应用代表用户获得访问的权限。同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。2012年10月,OAuth 2.0
协议正式发布为RFC 6749
。【百度百科】
在微服务架构
中,我们通常使用无状态
形式存储用户信息,比如JWT
,那么我们就想开发一个单独的认证授权服务
出来,此时我们还要考虑到这个认证授权服务
是在微服务架构
中产生的,那么我们肯定要考虑到这个认证授权服务
能授权我们访问哪些服务
呢?
白话理解专业名词
认证授权服务和资源服务
针对上面那个问题,就出现了认证授权服务
和资源服务
,所谓资源服务
就是授权后
我们能被访问的那些服务
,白话理解就是说资源服务
可以是订单服务,充值服务
等等,那么为什么需要定义资源服务
呢?
此时你是不是在想,既然认证通过
,那么理应可以访问所有的服务
,就像单体应用那样,认证通过
,只需要验证角色
就行了。
这要说到最近比较火的词,中台
,这和中台的理念极为相近
,无论前台后台,都直接调用这里的接口即可。如果你不了解这个词,没关系,我这里有白话解释。
比如,我这个公司,开发了很多个APP
,每个APP对应了不同的服务
,比如:快买啊APP
只能访问订单服务
和充值服务
,理财APP
可以访问充值服务
、提现服务
、购买理财产品服务
,虽然这些APP
可以共用账户的认证信息
,但我需要区分每个client
对应的resources
有哪些。又或者我授权第三方某个APP
可以访问我的某些服务
,此时这个APP
就是client资源
,它其中对应的resources
就是可以访问哪些我的服务
,此时我们将授权粒度分的更细了。
现在我们明白了,认证授权服务
它就只提供认证和授权,其中clients
对应的就是一个个APP
,resources
对应的就是各个服务
,我们在数据库中存着的信息就是每个client
包含哪些resources
和scopes
,关于scopes
我决定下面单说。
scope作用域
在上面对客户端进行资源区分
后,还有一个东西,scope(作用域)
,这玩意的作用会将资源近一步的区分
,在创建clients对应关系时
,还会添加一个scopes
,其意义在于我明确表示这个APP(client)访问资源服务(resource)的时候的作用域(scope)
。
比如:虽然快买啊APP
和理财APP
都能访问充值服务
,但是我可以规定充值服务
的银行卡充值
只能由理财APP访问
,此时,我在充值服务
中,将银行卡充值
方法加了一个判断"#oauth2.hasScope(\"licai\")"
,作用域
同理可以限制第三方APP
。
关于白话理解
无论我写的多白话,实际上你看的应该还是有点云里雾里的,因为这玩意确实挺绕的,所以下面我会给一个实际例子,理论+代码=Get。
搭建认证授权服务
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
编写认证配置(SecurityConfig)
实际上这就是你在springboot
单体应用中security
的配置。
它只负责认证账号密码
。
package com.doub1.authorizationserver.config;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setMaxAge(18000L);
source.registerCorsConfiguration("/**", corsConfiguration);
http.authorizeRequests().anyRequest().permitAll()
.and().csrf().disable().formLogin().and()
.cors().configurationSource(source).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
}
//为了省事 我没有写UserDetailsService
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("test").password(bCryptPasswordEncoder().encode("test123")).authorities("admin")
.and().passwordEncoder(bCryptPasswordEncoder());
}
}
配置授权服务
其中需要配置的,ClientDetailsService
,TokenStore
,AuthorizationServerTokenServices
等,详细信息我写在了注释内。
基于内存Token的授权服务配置
缺点:资源服务需要通过RemoteTokenService,转发Token,请求让我们验证。
package com.doub1.authorizationserver.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
public OAuth2Config() {
super();
}
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
//token 存储方式
//如果是内存,那么资源服务自己就无法验证信息
//因为token信息是存在我们的内存里
//资源服务那边定义TokenService的时候就会使用RemoteTokenService,通过我们来验证Token.
@Bean
TokenStore tokenStore(){
return new InMemoryTokenStore();
}
//可以自定义客户端信息服务 ClientDetailsService,我这里是测试栗子,可以使用内存方式创建
@Autowired
private ClientDetailsService clientDetailsService;
//配置token服务
@Bean
AuthorizationServerTokenServices tokenService(){
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
//设置ClientDetailsService
defaultTokenServices.setClientDetailsService(clientDetailsService);
//是否支持刷新token
defaultTokenServices.setSupportRefreshToken(true);
//设置token存储方式
defaultTokenServices.setTokenStore(tokenStore());
//设置token有效期
defaultTokenServices.setAccessTokenValiditySeconds(7200);
//设置token刷新的有效期(在时间范围内都可以刷新,哪怕token已经失效)
defaultTokenServices.setRefreshTokenValiditySeconds(7200*2);
return defaultTokenServices;
}
//配置ClientDetailsService,内存方式
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//客户端只有一个myApp,能访问的资源有a-service和b-service
//授权允许授权码,用户密码,客户端凭证,显式授权,允许刷新token
//并且myApp申请token后重定向地址为"/"绝对路径
clients.inMemory()
.withClient("myApp")
.secret(bCryptPasswordEncoder.encode("secret"))
.resourceIds("a-service","b-service")
.authorizedGrantTypes("authorization_code","password","client_credentials","implicit","refresh_token")
.scopes("all")
.autoApprove(false)
.redirectUris("/");
}
//配置安全项
//允许创建token和检查token
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.passwordEncoder(bCryptPasswordEncoder)
.allowFormAuthenticationForClients();
}
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
//.authorizationCodeServices(authorizationCodeServices())
/* 为空的时候会自动获取InMemoryAuthorizationCodeServices
* if (authorizationCodeServices == null) {
authorizationCodeServices = new InMemoryAuthorizationCodeServices();
}
return authorizationCodeServices;
* */
.tokenServices(tokenService())
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}
}
基于JwtToken的授权服务配置
无状态的,验证Token只要确保资源服务也使用相同的加密算法就可以校验。需要注意的是,认证配置那里需要将sessionCreationPolicy
改为无状态的,因为我们不需要存储。
package com.doub1.authorizationserver.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
public OAuth2Config() {
super();
}
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Bean
JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey("SIGNATURE_KEY");
return jwtAccessTokenConverter;
}
//token 存储方式
//我们使用JWT转换器,将其设置为无状态的
@Bean
TokenStore tokenStore(){
//return new InMemoryTokenStore();
return new JwtTokenStore(jwtAccessTokenConverter());
}
//可以自定义客户端信息服务 ClientDetailsService,我这里是测试栗子,可以使用内存方式创建
@Autowired
private ClientDetailsService clientDetailsService;
//配置token服务
@Bean
AuthorizationServerTokenServices tokenService(){
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setClientDetailsService(clientDetailsService);
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenStore(tokenStore());//TokenStore
//Token增强器链
TokenEnhancerChain tokenEnhancerChain=new TokenEnhancerChain();
//jwtAccessTokenConverter本身就继承TokenEnhancer,我们在链条后加了一个TokenEnhancer,让其在JWT中加入一些我们想加入的信息
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter(), new TokenEnhancer() {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String, Object> additionalInformation = accessToken.getAdditionalInformation();
System.out.println(additionalInformation);
additionalInformation.put("自定义信息","蛇皮哟");
((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(additionalInformation);
return accessToken;
}
}));
defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);
defaultTokenServices.setAccessTokenValiditySeconds(7200);
defaultTokenServices.setRefreshTokenValiditySeconds(7200*2);
return defaultTokenServices;
}
//配置ClientDetailsService,内存方式
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//客户端只有一个myApp,能访问的资源有a-service和b-service
//授权允许授权码,用户密码,客户端凭证,显式授权,允许刷新token
//并且myApp申请token后重定向地址为"/"绝对路径
clients.inMemory()
.withClient("myApp")
.secret(bCryptPasswordEncoder.encode("secret"))
.resourceIds("a-service","b-service")
.authorizedGrantTypes("authorization_code","password","client_credentials","implicit","refresh_token")
.scopes("all")
.autoApprove(false)
.redirectUris("/");
}
//配置安全项
//允许创建token和检查token
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.passwordEncoder(bCryptPasswordEncoder)
.allowFormAuthenticationForClients();
}
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
//.authorizationCodeServices(authorizationCodeServices())
/* 为空的时候会自动获取InMemoryAuthorizationCodeServices
* if (authorizationCodeServices == null) {
authorizationCodeServices = new InMemoryAuthorizationCodeServices();
}
return authorizationCodeServices;
* */
.tokenServices(tokenService())
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}
}
申请token
http://localhost:8080/oauth/
为gateway
重定向到的认证授权服务
,也就是上面那个栗子。路径你们自己看着改。
搭建资源服务
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
配置资源服务
资源服务就是以前的订单服务,充值服务等,我们是需要加上鉴权而已,我这里就是里面的a-service
.
远程调用认证授权服务的TokenService资源验证
package com.doub1.aservice.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Value("${spring.application.name}")
private String APPLICATION_NAME;
@Autowired
private RestTemplate restTemplate;
//远程token服务,这样会将token验证转发到认证授权服务去验证
@Bean
RemoteTokenServices tokenServices(){
RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setRestTemplate(restTemplate);
remoteTokenServices.setCheckTokenEndpointUrl("http://authorization-server/oauth/check_token");
remoteTokenServices.setClientId("myApp");
remoteTokenServices.setClientSecret("secret");
return remoteTokenServices;
}
//指定自己的resourceID,我们的APPLICATION_NAME就是a-service
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(APPLICATION_NAME).tokenServices(tokenServices()).stateless(true);
}
//鉴权配置,访问是否具有权限
@Override
public void configure(HttpSecurity http) throws Exception {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setMaxAge(18000L);
source.registerCorsConfiguration("/**", corsConfiguration);
http.authorizeRequests()
.anyRequest().access("#oauth2.hasScope(\"all\")")
.and().csrf().disable().cors().configurationSource(source)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
基于JWT的无状态资源验证
package com.doub1.aservice.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Value("${spring.application.name}")
private String APPLICATION_NAME;
@Autowired
private RestTemplate restTemplate;
@Bean
JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey("SIGNATURE_KEY");
return jwtAccessTokenConverter;
}
@Bean
TokenStore tokenStore() {
//return new InMemoryTokenStore();
return new JwtTokenStore(jwtAccessTokenConverter());
}
//指定自己的resourceID,我们的APPLICATION_NAME就是a-service
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
//这里的tokenServices直接换成tokenStore
//我还贴心的为你们准备了自定义验证失败的处理类
resources.resourceId(APPLICATION_NAME).tokenStore(tokenStore()).stateless(true)
.authenticationEntryPoint(new AuthExceptionEntryPoint()).accessDeniedHandler(new CustomAccessDeniedHandler());
//resources.resourceId(APPLICATION_NAME).tokenServices(tokenServices()).stateless(true);
}
//鉴权配置,访问是否具有权限
@Override
public void configure(HttpSecurity http) throws Exception {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setMaxAge(18000L);
source.registerCorsConfiguration("/**", corsConfiguration);
http.authorizeRequests()
.anyRequest().access("#oauth2.hasScope(\"all\")")
.and().csrf().disable().cors().configurationSource(source)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
自定义验证失败处理
认证异常
package com.doub1.aservice.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class AuthExceptionEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)throws ServletException {
Map map = new HashMap();
map.put("error", "401");
map.put("message", authException.getMessage());
map.put("path", request.getServletPath());
map.put("timestamp", String.valueOf(new Date().getTime()));
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
try {
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), map);
} catch (Exception e) {
throw new ServletException();
}
}
}
拒绝访问
package com.doub1.aservice.config;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component("customAccessDeniedHandler")
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
response.setContentType("application/json;charset=UTF-8");
Map map = new HashMap();
map.put("error", "400");
map.put("message", accessDeniedException.getMessage());
map.put("path", request.getServletPath());
map.put("timestamp", String.valueOf(new Date().getTime()));
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(objectMapper.writeValueAsString(map));
}
}
资源服务中如何获取用户信息
SecurityContextHolder的上下文中存着认证信息,获取也非常方便。
@RestController
public class HomeController {
@RequestMapping("/")
@PreAuthorize("hasAuthority(\"admin\")")
public String index(){
//SecurityContextHolder 直接就能拿到认证服务那边的信息了。是不是很简单
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication.getName();
}
}