我已经在我的spring Boot 微服务项目中实现了安全性,要求有两种类型的配置,一种用于用户请求(来自Angular ),另一种来自其他服务。设计是使用JWT标记用于用户请求,API密钥用于系统调用。
以下是配置文件(一个文件),但我也尝试将其拆分为两个文件,但没有任何影响:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Configuration
@Order(1)
public static class APISecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${my.api.key.header}")
private String principalRequestHeader;
@Value("${my.api.key.token}")
private String principalRequestValue;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors().disable().csrf().disable();
httpSecurity
.antMatcher("/api/users/**")
.authorizeRequests() //
.anyRequest().authenticated()
.and()
.addFilterBefore(new APIKeyAuthFilter(principalRequestHeader, principalRequestValue), UsernamePasswordAuthenticationFilter.class);
}
}
@Order(2)
@Configuration
public static class MySecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userDetailsService;
@Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/users/**");
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors().disable().csrf().disable();
httpSecurity
.authorizeRequests()
.antMatchers("/users/UserEmailExist", "/users/User/Add", "/users/Authenticate",
"/users/User/ChangePassword")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/users/**").hasAnyRole(ROLE_ADMIN_USER, ROLE_MANAGER_USER)
.anyRequest().authenticated()
.and()
.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
}
每个配置都有一个附加的过滤器,这里是api过滤器:
public class APIKeyAuthFilter extends GenericFilterBean {
private String principalRequestHeader;
private String principalRequestValue;
public APIKeyAuthFilter(String principalRequestHeader, String principalRequestValue) {
super();
this.principalRequestHeader = principalRequestHeader;
this.principalRequestValue = principalRequestValue;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if(request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
String apiKey = getApiKey((HttpServletRequest) request);
if(apiKey != null) {
if(apiKey.equals(principalRequestValue)) {
ApiKeyAuthenticationToken apiToken = new ApiKeyAuthenticationToken(apiKey, AuthorityUtils.NO_AUTHORITIES);
SecurityContextHolder.getContext().setAuthentication(apiToken);
} else {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(401);
httpResponse.getWriter().write("Invalid API Key");
return;
}
}
}
chain.doFilter(request, response);
}
}
下面是jwt(普通用户)的过滤器:
public class AuthTokenFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private MyUserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
String username = jwtUtils.getUserNameFromJwtToken(jwt);
MSUserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
logger.error("Cannot set user authentication: {}", e);
}
filterChain.doFilter(request, response);
}
}
我创建了两个不同的控制器,一个带有前缀/api/users,另一个带有前缀/users。
1.用户从Angular登录,获取jwt令牌并处理最终进入Jwt过滤器的请求,此场景看起来很好,没有任何问题,因为只要用户通过身份验证,就可以处理请求。
1.微服务将带有api-key的请求发送到带有/api/users前缀的url,该请求在正常用户结束的同一过滤器上结束,这是不正确的,如果没有JWT标记,他实际上能够继续到控制器并处理请求,而无需转到正确的过滤器。
我唯一的解决方案是只有一个过滤器,并处理api-key和jwt的头部,但它似乎不正确。我在网上看了看,并试图找出我做错了什么,但到目前为止没有线索。
2条答案
按热度按时间gg0vcinb1#
关于这个问题的更新,所以我希望它会有助于社会。
首先,我删除了下面的代码,这主要是解决问题:
该解决方案作为一个整体工作的方式是,第一个配置@Order(1)您定义了.antMatcher,这意味着该配置只对与前缀匹配的URL有效。场景1.来自Angular的用户只使用JWT过滤器。场景2. API用户将首先使用API过滤器!但是一旦完成(成功验证后)它仍然继续JWT过滤器,但因为它没有JWT过滤器,所以不执行任何操作。
我想避免其他过滤器的情况下,API调用,但解决方案的工作,问题解决了。我必须说,安全在Spring Boot 是最复杂的,我遇到了迄今为止从其他功能。
xkftehaa2#
因为
AuthTokenFilter
是用@Bean
示例化的,这导致过滤器被添加到ApplicationFilterChain
,所以在处理完APIKeyAuthFilter
之后,它也可以进入AuthTokenFilter
。