我有一个位于控制器前面的请求过滤器。该过滤器检索用户个人资料,并在具有请求范围的userProfile
组件上设置属性,然后传递到下一个过滤器。
当尝试从过滤器内部访问userProfile
时,该属性尚未成功自动接线。
当尝试从过滤器内部自动连接userProfile
时,我看到以下异常:
org.springframework.beans.factory.BeanCreationException:创建名称为'scopedTarget.userProfile'的bean时出错:当前线程的作用域'request'未激活;如果您打算从单例中引用它,请考虑为此bean定义作用域代理。嵌套异常为java.lang.IllegalStateException:未找到线程绑定的请求:您是在实际Web请求之外引用请求属性,还是在原始接收线程之外处理请求?如果您实际上是在Web请求中操作并且仍然收到此消息,则您的代码可能正在DispatcherServlet外部运行:在这种情况下,请使用RequestContextListener或RequestContextFilter公开当前请求。
但是,当尝试从控制器内部访问userProfile
时,该属性已成功自动接线。
如何成功自动将过滤器内的userProfile
组件自动接线?
请求过滤器:
@Component
public class JwtAuthenticationFilter extends GenericFilterBean implements Filter {
@Autowired
public UserProfile userProfile;
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain next) throws IOException, ServletException {
....
userProfile
.username(authorizedUser.username())
.email(authorizedUser.email())
.firstName(authorizedUser.firstName())
.lastName(authorizedUser.lastName());
}
}
控制器:
@CrossOrigin
@RestController
@RequestMapping("/users")
public class UsersController {
@Autowired
public UserProfile userProfile;
@GetMapping(
path = "/current",
produces = MediaType.APPLICATION_JSON_VALUE
)
@ResponseStatus(HttpStatus.OK)
public String currentUser() throws ResponseFormatterException {
System.out.println(userProfile.email());
}
}
用户个人资料:
@Component
@RequestScope
public class UserProfile {
@Getter @Setter
@Accessors(fluent = true)
@JsonProperty("username")
private String username;
@Getter @Setter
@Accessors(fluent = true)
@JsonProperty("email")
private String email;
@Getter @Setter
@Accessors(fluent = true)
@JsonProperty("firstName")
private String firstName;
@Getter @Setter
@Accessors(fluent = true)
@JsonProperty("lastName")
private String lastName;
}
安全配置:
@Configuration
@EnableWebSecurity
public class SecurityConfigurator extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticatingFilter jwtAuthenticatingFilter;
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(getAuthenticator());
}
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/actuator/**")
.antMatchers("/favicon.ico");
}
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/actuator/**").permitAll()
.antMatchers("/favicon.ico").permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.addFilterBefore(getFilter(), SessionManagementFilter.class)
.authenticationProvider(getAuthenticator())
.exceptionHandling()
.authenticationEntryPoint(new HttpAuthenticationEntryPoint());
}
protected AbstractAuthenticator getAuthenticator() {
return new JwtAuthenticator();
}
protected AuthenticatingFilter getFilter() {
return jwtAuthenticatingFilter;
}
}
答案 0 :(得分:0)
当Spring Boot在Filter
中检测到ApplicationContext
时,它将自动在servlet容器的过滤器链中注册它。但是,您不希望在这种情况下发生这种情况,因为该过滤器是Spring Security过滤器链的一部分。
要解决此问题,请执行以下操作:
@Component
@Autowire
的{{1}} JwtAuthenticationFilter
创建一个@Bean
方法JwtAuthenticationFilter
创建@Bean
方法以禁用注册过程。 FilterRegistrationBean
然后在您的代码中代替
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Bean
public FilterRegistrationBean<JwtAuthenticationFilter> jwtAuthenticationFilterRegistrationBean() {
FilterRegistrationBean<JwtAuthenticationFilter> frb = new JwtAuthenticationFilter(jwtAuthenticationFilter());
frb.setEnabled(false);
return frb;
}
而不是getFilter
方法。
答案 1 :(得分:0)
我认为问题可能在于您试图将请求范围的Bean(较小的作用域)注入到单例范围的Bean(较大的作用域)中。导致此无效的原因有两个:
您可以使用javax.inject.Provider来解决此问题,以根据需要延迟注入请求范围的bean。
@Component
public class JwtAuthenticationFilter extends GenericFilterBean implements Filter {
@Autowired
public Provider<UserProfile> userProfileProvider;
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain next) throws IOException, ServletException {
....
userProfileProvider.get()
.username(authorizedUser.username())
.email(authorizedUser.email())
.firstName(authorizedUser.firstName())
.lastName(authorizedUser.lastName());
}
}
Spring具有类似的接口org.springframework.beans.factory.ObjectFactory
,如果您在设置Provider的依赖项时遇到困难,可以使用它。
@Component
public class JwtAuthenticationFilter extends GenericFilterBean implements Filter {
@Autowired
public ObjectFactory<UserProfile> userProfileFactory;
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain next) throws IOException, ServletException {
....
userProfileFactory.getObject()
.username(authorizedUser.username())
.email(authorizedUser.email())
.firstName(authorizedUser.firstName())
.lastName(authorizedUser.lastName());
}
}