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)
}
谢谢!
使用时我也有类似问题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)