java - @autowired 在 UsernamePasswordAuthenticationFilter Spring Boot 中为 null

标签 java spring spring-mvc spring-boot spring-security

<分区>

我是 spring boot 的新手,我正在尝试在我的 spring boot 应用程序中使用 spring security 实现身份验证过滤器,但我不明白为什么在 tokenService 属性中使用 @autowired 注释时它为空,有人可以解释一下吗对我来说为什么会这样? java 代码中的正确配置是什么(我没有使用 xml 文件),以便 spring tokenService 属性不为空。 接下来我将向您展示感兴趣的类:

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+"}");}}

这是我的 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;
}
}

这是我的配置类:

@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;
}

谢谢!

最佳答案

我在使用 BasicAuthenticationFilter 时也遇到了类似的问题。请参阅下面我用来使事情正常运行的解决方案。

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

public JWTAuthenticationFilter(AuthenticationManager authenticationManager, ApplicationContext ctx) {
    this.authenticationManager = authenticationManager;
    this.tokenService= ctx.getBean(TokenService.class);
}

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

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

采用这种设计方法的原因 - Spring security 支持的身份验证处理机制基于扩展 GenericFilterBean 的过滤器.根据文档(强调我的)

This generic filter base class has no dependency on the Spring ApplicationContext concept. Filters usually don't load their own context but rather access service beans from the Spring root application context, accessible via the filter's ServletContext (see WebApplicationContextUtils).

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

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

P.S.:您可以考虑将 JWTAuthenticationFilter 用作 @Bean,并在配置中将其引用为 .addFilter(jwtAuthFilter( ))

@Bean
public JWTAuthenticationFilter jwtAuthFilter() throws Exception {
    return new JWTAuthenticationFilter(authenticationManager(), getApplicationContext());
}

关于java - @autowired 在 UsernamePasswordAuthenticationFilter Spring Boot 中为 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48107157/

相关文章:

spring - 如何配置Spring Boot和Spring Security同时支持表单登录和Google OAuth2登录

java - 查找并获取网页发出的所有资源请求的响应码 - Java, Selenium

java - 迭代以在索引处查找 Map 条目?

java - System.out 被声明为 static Final 并用 null 初始化?

java - spring mvc中如何使用静态资源?

spring-mvc - Siteminder 的 Spring Security Java 配置

java - 在 hibernate 状态下删除

java - 通过构造函数创建 bean 时出错 Bean 实例化失败

jquery - 对 Spring MVC Controller 的 AJAX POST 请求不起作用

spring-mvc - Spring Boot自定义favicon.ico未显示