带有 JWT 的 Java Spring 多个 @Autowired MongoRepository 用法

Posted

技术标签:

【中文标题】带有 JWT 的 Java Spring 多个 @Autowired MongoRepository 用法【英文标题】:Java Spring Multiple @Autowired MongoRepository Usages with JWT 【发布时间】:2019-08-09 11:39:40 【问题描述】:

JWTAuthenticationFilter.java 中的successfulAuthentication 函数给出了一个空指针异常。你明白为什么这会是一个问题吗?使用同一个 bean 进行自动装配有问题吗?

这是我目前的项目结构:

-com
  -register
    -RegisterController.java
  -security
    -JWTAuthenticationFilter.java
    -JWTAuthorizationFilter.java
    -SecurityConstants.java
    -WebSecurity.java
  -user
    -User.java
    -UserRepository.java
    -UserService.java
  -Application.java

Application.java

@Configuration
@SpringBootApplication
public class Application 
  @Bean
  public BCryptPasswordEncoder bCryptPasswordEncoder() 
      return new BCryptPasswordEncoder();
  

  public static void main(String[] args) 
    SpringApplication.run(Application.class, args);
  

UserRepository.java

@Repository
public interface UserRepository extends MongoRepository<User, String> 
  User findByUsername(String name);
  User findByEmail(String Email);
  User findBy_id(ObjectId id);

用户服务.java

@Service
public class UserService implements UserDetailsService 
  @Autowired
  private UserRepository userRepository;

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
    User user = this.userRepository.findByUsername(username);
    if(user == null)
        return null;
    List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority("user"));
    return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
  

  public User getUserByUsername(String username) 
    return this.userRepository.findByUsername(username);
  

  public User getUserBy_id(ObjectId _id) 
    return userRepository.findBy_id(_id);
  

  public void saveUser(User newUser)
    userRepository.save(newUser);
  

用户.java

@Document
public final class User 

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private ObjectId _id;
  private String email;
  private String username;
  private String password;
  private AccountProperties accountProperties;
  private Address address;
  private List<Pet> pets = new ArrayList<>();
  private String phoneNumber;

  public User() 

  public User(@JsonProperty("email") String email, @JsonProperty("username") String username,
            @JsonProperty("password") String password) 
    this.email = email;
    this.username = username;
    this.password = password;
  

  public String get_id()  return _id.toHexString();

  getters and setters() ...

JWTAuthenticationFilter.java

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter 

  @Autowired
  private UserRepository userRepo;
  private AuthenticationManager authenticationManager;

  JWTAuthenticationFilter(AuthenticationManager authenticationManager) 
    this.authenticationManager = authenticationManager;
  

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

        return authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        creds.getUsername(),
                        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 username = ((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername();

    String token = JWT.create()
            .withSubject(username)
            .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
            .sign(HMAC512(SECRET.getBytes()));
    res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
    User u = uRepo.findByUsername("admin");
    res.getWriter().write(
            "\"" + SecurityConstants.HEADER_STRING + "\":\"" + SecurityConstants.TOKEN_PREFIX+token + "\"," +
            "\"" + "ObjectID" + "\":\"" + u.get_id() + "\""
    );
  

JWTAuthorizationFilter.java

public class JWTAuthorizationFilter extends BasicAuthenticationFilter 

  public JWTAuthorizationFilter(AuthenticationManager authManager) 
    super(authManager);
  

  @Override
  protected void doFilterInternal(HttpServletRequest req,
                                HttpServletResponse res,
                                FilterChain chain) throws IOException, ServletException 
    String header = req.getHeader(HEADER_STRING);

    if (header == null || !header.startsWith(TOKEN_PREFIX)) 
        chain.doFilter(req, res);
        return;
    

    UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
 SecurityContextHolder.getContext().setAuthentication(authentication);
    chain.doFilter(req, res);
  

  private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) 
    String token = request.getHeader(HEADER_STRING);
    if (token != null) 
        // parse the token.
        String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
                .build()
                .verify(token.replace(TOKEN_PREFIX, ""))
                .getSubject();

        if (user != null) 
            return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
        
        return null;
    
    return null;
  

WebSecurity.java

@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter 
  @Autowired
  private UserService userDetailsService;
  @Autowired
  private BCryptPasswordEncoder bCryptPasswordEncoder;

  @Override
  protected void configure(HttpSecurity http) throws Exception 
    http.cors().and().csrf().disable().authorizeRequests()
            .antMatchers(HttpMethod.POST, SIGN_UP_URL).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(this.userDetailsService).passwordEncoder(this.bCryptPasswordEncoder);

  @Bean
  CorsConfigurationSource corsConfigurationSource() 
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
    return source;
  

SecurityConstants.java

public class SecurityConstants 
  public static final String SECRET = "SecretKeyToGenJWTs";
  public static final long EXPIRATION_TIME = 864_000_000; // 10 days
  public static final String TOKEN_PREFIX = "Bearer ";
  public static final String HEADER_STRING = "Authorization";
  public static final String SIGN_UP_URL = "/users/sign-up";

RegisterController.java

@RestController
@RequestMapping("/users")
public class RegisterController 
  @Autowired
  private UserService userService;
  @Autowired
  private BCryptPasswordEncoder bCryptPasswordEncoder;

  @PostMapping("/sign-up")
  public void signUp(@RequestBody User user) 
    if (user.getPassword() == null || user.getUsername() == null)
        return;
    user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
    userService.saveUser(user);
  

【问题讨论】:

【参考方案1】:

不确定这是否是您的问题的根本原因,但我从未在主应用程序中看到 @Configuration。我会尝试将其移至单独的配置类,看看是否有帮助

【讨论】:

我对Java Spring不是太熟悉,所以你知道我们是否需要一个配置类吗?如果是这样,WebSecurity 类是否可以使用 @Configuration 进行注释? 您应该能够将 @Configuration 添加到 WebSecurity 类中,我在我团队的一个 java 项目中就有了。如果我在使用 @Configuration 注释的类中使用 @Autowired,我的 IDE 会抱怨,因此这可能不适用于您的场景。【参考方案2】:

使用@Component 注释JWTAuthenticationFilter 或在配置文件中添加@Bean。好像没有创建对象

【讨论】:

【参考方案3】:

问题是你没有将JWTAuthenticationFilter定义为Bean,所以spring不会在里面注入依赖。

您可以手动在过滤器中获取 bean。来自 GenericFilterBean javadoc:

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

或者你可以把它变成bean。但如果您使用的是 Spring Boot,请考虑:

Spring Security 内部的所有过滤器对于容器来说都是未知的这一事实很重要,尤其是在 Spring Boot 应用程序中,默认情况下,所有 Filter 类型的 @Beans 都会自动注册到容器中。因此,如果您想将自定义过滤器添加到安全链中,则需要不将其设为 @Bean 或将其包装在显式禁用容器注册的 FilterRegistrationBean 中。

【讨论】:

以上是关于带有 JWT 的 Java Spring 多个 @Autowired MongoRepository 用法的主要内容,如果未能解决你的问题,请参考以下文章

多个WebSecurityConfigurerAdapters:spring security中的JWT认证和表单登录

带有 spring-boot 和 spring-security 的 JWT

带有 JWT 令牌的 Spring Security 在每个请求上加载 spring UserDetails 对象

用于 REST API 的带有 JWT 的 Spring Security

带有 JWT 令牌的 Spring Security 和 Websocket

带有 JWT AuthenticationCredentialsNotFoundException 的 Spring 安全配置