pom 文件配置
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.6.4version>
<relativePath/>
parent>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.security.oauthgroupId>
<artifactId>spring-security-oauth2artifactId>
<version>2.3.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-jwtartifactId>
<version>1.1.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.glassfish.jaxbgroupId>
<artifactId>jaxb-runtimeartifactId>
<version>2.3.6version>
dependency>
oauth 2.0 服务端可以参考文章: Spring Security oauth2.0 服务端
建议 token 使用 JWT,JWT 可以使用算数的方法进行校验,不需要对颁发 token 的服务端发起验证请求
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(signKey);
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Configuration
@EnableResourceServer
public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
private static String resourceId = "oauth2-resource";
private static List<String> ignoreUrl = new ArrayList<>();
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
super.configure(resources);
resources.tokenStore(tokenStore());
resources.resourceId(resourceId);
}
@Override
public void configure(HttpSecurity http) throws Exception {
List<String> ignoreUrl = new ArrayList<>();
ignoreUrl.add("/test");
String[] ignoreUrls = ignoreUrl.toArray(new String[ignoreUrl.size()]);
http.authorizeRequests()
.antMatchers(ignoreUrls).permitAll()
.anyRequest().authenticated();
}
}
默认转换器 DefaultUserAuthenticationConverter,JWT 返回的是 user_name 的值,代码如下所示
public class DefaultUserAuthenticationConverter implements UserAuthenticationConverter {
...
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME)) {
Object principal = map.get(USERNAME);
Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
if (userDetailsService != null) {
UserDetails user = userDetailsService.loadUserByUsername((String) map.get(USERNAME));
authorities = user.getAuthorities();
principal = user;
}
return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
}
return null;
}
}
通过 SecurityContextHolder 可以获取
@GetMapping("/test/username")
public Result<Object> getUserName() throws Exception {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return Result.success("成功!", authentication);
}
通过上述已知,默认情况 JWT 只返回用户的用户名,但实际情况我们希望返回服务端传入 JWT 的实体,即实现 UserDetails 接口的类
自定义用户身份验证转换器
public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter {
@Override
public Authentication extractAuthentication(Map<String, ?> map) {
String userName = (String) map.get(USERNAME);
User principal = new User( userName,"N/A", new ArrayList<>());
return new UsernamePasswordAuthenticationToken(principal, "N/A", new ArrayList<>());
}
}
把刚新建的转换器添加到 JwtAccessTokenConverter 里
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
CustomUserAuthenticationConverter userAuthenticationConverter = new CustomUserAuthenticationConverter();
DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
accessTokenConverter.setUserTokenConverter(userAuthenticationConverter);
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(signKey);
converter.setAccessTokenConverter(accessTokenConverter);
return converter;
}
获取实体
@GetMapping("/test/username")
public Result<Object> getUserName() throws Exception {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User user = (User) authentication.getPrincipal();
return Result.success("成功!", authentication);
}