在UsernamePasswordAuthenticationFilter Spring Boot中@autowired为null

Ale*_*Dev 3 java spring spring-mvc spring-security spring-boot

我是Spring Boot的新手,我试图在Spring Boot应用程序中使用Spring Security实现身份验证过滤器,但我不明白为什么在tokenService属性中使用@autowired批注时它为null,有人可以向我解释为什么有时候是这样的?以及Java代码中的正确配置是什么(我没有使用xml文件),以便spring tokenService属性不为null。接下来,我将向您展示感兴趣的类别:

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";

private AuthenticationManager authenticationManager;

@Autowired
private TokenService tokenService;//this is null

public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
    this.authenticationManager = authenticationManager;
}

@Override
public Authentication attemptAuthentication(HttpServletRequest req,
                                            HttpServletResponse res) throws AuthenticationException {
    try {
        LoginDto creds = new ObjectMapper()
                .readValue(req.getInputStream(), LoginDto.class);

        return authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        creds.getEmail(),
                        creds.getPassword(),
                        new ArrayList<>())
        );
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

@Override
protected void successfulAuthentication(HttpServletRequest req,
                                        HttpServletResponse res,
                                        FilterChain chain,
                                        Authentication auth) throws IOException, ServletException {
String token=tokenService.createAuthenticationToken(auth);//nullpointer exception here!
        long expirationTime=tokenService.getExpirationTime(token);
    res.addHeader("Access-Control-Expose-Headers", "Authorization");
        res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
    res.setHeader("Content-Type", "application/json");
        res.getWriter().print("{\"expiresIn\": "+expirationTime+"}");}}
Run Code Online (Sandbox Code Playgroud)

这是我的TokenService类:

@Component
public class TokenService implements Serializable {

private static final long serialVersionUID = 1L;

long expirationTime = new Long(86400000); // 1 day
public static final String SECRET = "SecretKeyToGenJWTs";
public static final long EXPIRATION_TIME = 86_400_000; // 1 day
public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";

@Autowired
private UserService userService;

@Autowired
private ProviderService providerService;

public TokenService() {

}

public String createAuthenticationToken(Authentication auth) {
    String token = Jwts.builder().setSubject(((User) auth.getPrincipal()).getUsername()).setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + expirationTime))
            .signWith(SignatureAlgorithm.HS512, SECRET.getBytes()).compact();
    return token;
}

public long getExpirationTime(String token) {
    Claims claims = Jwts.parser().setSigningKey(SECRET.getBytes()).parseClaimsJws(token).getBody();
    Integer intDate = (Integer) claims.get("exp");
    long tokenDate = (intDate.longValue() * 1000);
    return tokenDate;
}

public String getSubjectFromAuthorizationToken(String token) {
    String subject = Jwts.parser().setSigningKey(SECRET.getBytes()).parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
            .getBody().getSubject();
    return subject;
}
}
Run Code Online (Sandbox Code Playgroud)

这是我的配置类:

@Configuration
@EnableWebSecurity
@ComponentScan({"com.espiritware.opusclick.security"})

public class WebSecurity extends WebSecurityConfigurerAdapter {

public static final String USER_REGISTRATION_URL = "/v1/users/registration";
public static final String PROVIDER_REGISTRATION_URL = "/v1/providers/registration";
public static final String CONFIRM_REGISTRATION_URL = "/v1/registrationConfirm";
public static final String SEND_RESET_PASSWORD_EMAIL = "/v1/sendResetPasswordEmail";
public static final String RESET_PASSWORD = "/v1/resetPassword";
public static final String LOGIN = "/v1/login";


private UserDetailsService userDetailsService;

private BCryptPasswordEncoder bCryptPasswordEncoder;

public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
    this.userDetailsService = userDetailsService;
    this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and().csrf().disable()
            .authorizeRequests().antMatchers(HttpMethod.POST, USER_REGISTRATION_URL).permitAll()
            .antMatchers(HttpMethod.POST, PROVIDER_REGISTRATION_URL).permitAll()
            .antMatchers(HttpMethod.GET, CONFIRM_REGISTRATION_URL).permitAll()
            .antMatchers(HttpMethod.POST, SEND_RESET_PASSWORD_EMAIL).permitAll()
            .antMatchers(HttpMethod.POST, RESET_PASSWORD).permitAll()
            .anyRequest().authenticated().and()
            .addFilter(new JWTAuthenticationFilter(authenticationManager()))
            .addFilter(new JWTAuthorizationFilter(authenticationManager()))
            // this disables session creation on Spring Security
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
    return source;
}
Run Code Online (Sandbox Code Playgroud)

}

谢谢!

Bon*_*ond 6

使用时我也有类似问题BasicAuthenticationFilter。请参阅下面我用来使事情工作的解决方案。

更新JWTAuthenticationFilter构造函数以接受应用程序上下文作为参数之一

public JWTAuthenticationFilter(AuthenticationManager authenticationManager, ApplicationContext ctx) {
    this.authenticationManager = authenticationManager;
    this.tokenService= ctx.getBean(TokenService.class);
}
Run Code Online (Sandbox Code Playgroud)

更新配置以将传递ApplicationContext给过滤器实例

@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
        // .. some settings
        .addFilter(new JWTAuthenticationFilter(authenticationManager(), getApplicationContext()))
        // some other settings    
}
Run Code Online (Sandbox Code Playgroud)

采用这种设计方法的原因 -Spring安全性支持的Authentication处理机制基于扩展的过滤器GenericFilterBean。根据文档(重点是我的

这个通用的过滤器基类依赖于Spring ApplicationContext概念。过滤器通常不加载自己的上下文,而是从Spring根应用程序上下文访问服务bean,可通过过滤器的ServletContext访问(请参阅WebApplicationContextUtils)。

因此,我们需要向ApplicationContext过滤器提供,以访问服务bean。

如果您需要更多信息,请在评论中告知。

PS:您也可以考虑使用JWTAuthenticationFilteras @Bean并在配置中将其引用为.addFilter(jwtAuthFilter())

@Bean
public JWTAuthenticationFilter jwtAuthFilter() throws Exception {
    return new JWTAuthenticationFilter(authenticationManager(), getApplicationContext());
}
Run Code Online (Sandbox Code Playgroud)