如何使用 Spring 安全性实现具有两级令牌身份验证的 Spring 引导微服务?
How to implement Spring Boot Microservice with two levels of token authentication using Spring Security?
您好团队,
我正在开发一个 Spring 引导项目,我想在其中使用 Spring 安全性 + JWT 设置两个级别的令牌身份验证。
我的一些同事已经使用 Dropwizard 框架构建了一个类似的应用程序。
我想在我的 Spring 引导项目中实现相同的体系结构。我在这个问题的末尾添加了 link 到 API 的架构。
我可以使用 Spring Boot(使用 Spring Security + JWT)设置第一级令牌身份验证,但我无法找到设置第二级的正确方法令牌认证。
我尝试搜索相关文章,但没有找到。
如果您可以分享在 Spring 引导(使用 Spring 安全性)中实现两个级别的令牌身份验证的代码片段以便更好地理解,将会很有帮助。
感谢期待!
以下是概念验证,可实现您的需求。第一个class是安全配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
public static final List<String> WHITE_LIST = asList("/authenticate");
public static final List<String> TOKEN1_LIST = asList("/get-access-token");
public static final List<String> TOKEN2_LIST = asList("/add-new-user");
@Autowired
private Token1Filter token1Filter;
@Autowired
private Token2Filter token2Filter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
// Make sure we use stateless session; session won't be used to store user's state
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// Handle an authorized attempts
.exceptionHandling().authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.authorizeRequests()
// List of services do not require authentication
.antMatchers(OPTIONS).permitAll()
.antMatchers(GET, WHITE_LIST.toArray(new String[WHITE_LIST.size()])).permitAll()
// Any other request must be authenticated
.anyRequest().authenticated()
.and()
.addFilterBefore(token1Filter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(token2Filter, UsernamePasswordAuthenticationFilter.class);
}
}
如您所见,不同的 Urls 包含在不同的属性中,并且与它们相关的每个 List
都在 HttpSecurity
class 中配置。用于管理每个子集的过滤器,我的意思是,使用 Jwt 1 和 Jwt 2 保护的 Urls 如下:
@AllArgsConstructor
@Component
public class Token1Filter extends OncePerRequestFilter {
private static final String TOKEN_PREFIX = "Bearer ";
@Override
protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
getJwt(request)
.ifPresent(jwt1 -> {
/**
* Here you can use functionality to check provided Jwt token 1,
* adding included data into Spring SecurityContextHolder.
*/
// Used for testing purpose
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("testUserToken1", null, new ArrayList<>())
);
});
filterChain.doFilter(request, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return !WebSecurityConfiguration.TOKEN1_LIST.contains(request.getRequestURI());
}
private Optional<String> getJwt(HttpServletRequest request) {
return ofNullable(request)
.map(r -> r.getHeader(AUTHORIZATION))
.filter(Predicate.not(String::isEmpty))
.map(t -> t.replace(TOKEN_PREFIX, ""))
.filter(Predicate.not(String::isEmpty));
}
}
@AllArgsConstructor
@Component
public class Token2Filter extends OncePerRequestFilter {
private static final String TOKEN_PREFIX = "Bearer ";
@Override
protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
getJwt(request)
.ifPresent(jwt2 -> {
/**
* Here you can use functionality to check provided Jwt token 2,
* adding included data into Spring SecurityContextHolder.
*/
// Used for testing purpose
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("testUserToken2", null,
asList(new SimpleGrantedAuthority("ADMIN")))
);
});
filterChain.doFilter(request, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return !WebSecurityConfiguration.TOKEN2_LIST.contains(request.getRequestURI());
}
private Optional<String> getJwt(HttpServletRequest request) {
return ofNullable(request)
.map(r -> r.getHeader(AUTHORIZATION))
.filter(Predicate.not(String::isEmpty))
.map(t -> t.replace(TOKEN_PREFIX, ""))
.filter(Predicate.not(String::isEmpty));
}
}
每个都包含一个 shouldNotFilter
方法的实现,以了解它是否必须管理当前请求。并包含关于您需要添加功能以提取和验证提供的 Jwt 令牌的位置的评论。
最后,只有一个虚拟控制器来测试每个用例:
@AllArgsConstructor
@RestController
public class TestController {
@GetMapping("/authenticate")
public ResponseEntity<String> authenticate(@RequestParam String username, @RequestParam String password) {
return new ResponseEntity("[authenticate] Testing purpose", OK);
}
@GetMapping("/get-access-token")
public ResponseEntity<String> getAccessToken() {
return new ResponseEntity("[get-access-token] Testing purpose", OK);
}
@GetMapping("/add-new-user")
@PreAuthorize("hasAuthority('ADMIN')")
public ResponseEntity<String> addNewUser() {
return new ResponseEntity("[add-new-user] Testing purpose", OK);
}
}
可以添加一些改进,但这是一个很好的初始点,可以按预期工作。
您好团队,
我正在开发一个 Spring 引导项目,我想在其中使用 Spring 安全性 + JWT 设置两个级别的令牌身份验证。
我的一些同事已经使用 Dropwizard 框架构建了一个类似的应用程序。
我想在我的 Spring 引导项目中实现相同的体系结构。我在这个问题的末尾添加了 link 到 API 的架构。
我可以使用 Spring Boot(使用 Spring Security + JWT)设置第一级令牌身份验证,但我无法找到设置第二级的正确方法令牌认证。
我尝试搜索相关文章,但没有找到。
如果您可以分享在 Spring 引导(使用 Spring 安全性)中实现两个级别的令牌身份验证的代码片段以便更好地理解,将会很有帮助。
感谢期待!
以下是概念验证,可实现您的需求。第一个class是安全配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
public static final List<String> WHITE_LIST = asList("/authenticate");
public static final List<String> TOKEN1_LIST = asList("/get-access-token");
public static final List<String> TOKEN2_LIST = asList("/add-new-user");
@Autowired
private Token1Filter token1Filter;
@Autowired
private Token2Filter token2Filter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
// Make sure we use stateless session; session won't be used to store user's state
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// Handle an authorized attempts
.exceptionHandling().authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.authorizeRequests()
// List of services do not require authentication
.antMatchers(OPTIONS).permitAll()
.antMatchers(GET, WHITE_LIST.toArray(new String[WHITE_LIST.size()])).permitAll()
// Any other request must be authenticated
.anyRequest().authenticated()
.and()
.addFilterBefore(token1Filter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(token2Filter, UsernamePasswordAuthenticationFilter.class);
}
}
如您所见,不同的 Urls 包含在不同的属性中,并且与它们相关的每个 List
都在 HttpSecurity
class 中配置。用于管理每个子集的过滤器,我的意思是,使用 Jwt 1 和 Jwt 2 保护的 Urls 如下:
@AllArgsConstructor
@Component
public class Token1Filter extends OncePerRequestFilter {
private static final String TOKEN_PREFIX = "Bearer ";
@Override
protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
getJwt(request)
.ifPresent(jwt1 -> {
/**
* Here you can use functionality to check provided Jwt token 1,
* adding included data into Spring SecurityContextHolder.
*/
// Used for testing purpose
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("testUserToken1", null, new ArrayList<>())
);
});
filterChain.doFilter(request, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return !WebSecurityConfiguration.TOKEN1_LIST.contains(request.getRequestURI());
}
private Optional<String> getJwt(HttpServletRequest request) {
return ofNullable(request)
.map(r -> r.getHeader(AUTHORIZATION))
.filter(Predicate.not(String::isEmpty))
.map(t -> t.replace(TOKEN_PREFIX, ""))
.filter(Predicate.not(String::isEmpty));
}
}
@AllArgsConstructor
@Component
public class Token2Filter extends OncePerRequestFilter {
private static final String TOKEN_PREFIX = "Bearer ";
@Override
protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
getJwt(request)
.ifPresent(jwt2 -> {
/**
* Here you can use functionality to check provided Jwt token 2,
* adding included data into Spring SecurityContextHolder.
*/
// Used for testing purpose
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("testUserToken2", null,
asList(new SimpleGrantedAuthority("ADMIN")))
);
});
filterChain.doFilter(request, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return !WebSecurityConfiguration.TOKEN2_LIST.contains(request.getRequestURI());
}
private Optional<String> getJwt(HttpServletRequest request) {
return ofNullable(request)
.map(r -> r.getHeader(AUTHORIZATION))
.filter(Predicate.not(String::isEmpty))
.map(t -> t.replace(TOKEN_PREFIX, ""))
.filter(Predicate.not(String::isEmpty));
}
}
每个都包含一个 shouldNotFilter
方法的实现,以了解它是否必须管理当前请求。并包含关于您需要添加功能以提取和验证提供的 Jwt 令牌的位置的评论。
最后,只有一个虚拟控制器来测试每个用例:
@AllArgsConstructor
@RestController
public class TestController {
@GetMapping("/authenticate")
public ResponseEntity<String> authenticate(@RequestParam String username, @RequestParam String password) {
return new ResponseEntity("[authenticate] Testing purpose", OK);
}
@GetMapping("/get-access-token")
public ResponseEntity<String> getAccessToken() {
return new ResponseEntity("[get-access-token] Testing purpose", OK);
}
@GetMapping("/add-new-user")
@PreAuthorize("hasAuthority('ADMIN')")
public ResponseEntity<String> addNewUser() {
return new ResponseEntity("[add-new-user] Testing purpose", OK);
}
}
可以添加一些改进,但这是一个很好的初始点,可以按预期工作。