【问题标题】:@Autowired failed Issue in Spring boot@Autowired 在 Spring Boot 中失败的问题
【发布时间】:2021-10-14 15:42:27
【问题描述】:

我正在尝试从我的 MySecurityConfiguration 类中为 JWT 自动装配 AuthenticationManager 组件,因为它失败了。谁能建议我根本原因或如何解决这个问题。

MySecurtyConfiguration.java

package com.expensesd.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.expenses.demo.service.UserDetailServiceImplementation;

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MySecurityConfigurations extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private JwtAutenticationEntryyPoint unauthorizedHandler;
    
    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;
    @Autowired
    private UserDetailServiceImplementation userDetailServiceImplementation;
    
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    
    @Override
    @Bean
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(this.userDetailServiceImplementation).passwordEncoder(passwordEncoder());
    }
    
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
        .cors().disable()
        .authorizeRequests()
        .antMatchers("/generate-token","/user/").permitAll()
        .antMatchers(HttpMethod.OPTIONS).permitAll()
        .anyRequest().authenticated()
        .and()
        .exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
    
    
}

AuthenticateController

package com.expenses.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.expenses.demo.modal.JwtRequest;
import com.expenses.demo.modal.JwtResponse;
import com.expenses.demo.service.UserDetailServiceImplementation;
import com.expensesd.demo.config.JwtUtil;

@RestController
public class AuthenticateController {
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Autowired
    private UserDetailServiceImplementation userDetailServiceImplementation;
    
    
    @Autowired
    private JwtUtil jwtUtils;
    
    
    //generate token
    @PostMapping("/generate-token")
    public ResponseEntity<?> generateToken(@RequestBody JwtRequest jwtRequest) throws Exception{
        try {
            this.authenticate(jwtRequest.getUsername(),jwtRequest.getPassword());
        } catch (UsernameNotFoundException e) {
            e.printStackTrace();
            System.out.println("User Not Found");
        }
        
        //Done Authenticate
        
        UserDetails userDetails =   this.userDetailServiceImplementation.loadUserByUsername(jwtRequest.getUsername());
        String token = this.jwtUtils.generateToken(userDetails);
        return ResponseEntity.ok(new JwtResponse(token));
    }
    
    private void authenticate(String username,String password) throws Exception {
        
        try {
            
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
            
        } catch (DisabledException e) {
            throw new Exception("User Disable" +e.getMessage());
        }catch (BadCredentialsException e) {
            throw new Exception("Invalid Credentials" +e.getMessage());
        }
        
    }
}


UserDetailServiceImplementation.java

package com.expenses.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.expenses.demo.modal.User;
import com.expenses.demo.repository.UserRepository;

@Service
public class UserDetailServiceImplementation implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;
    
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = this.userRepository.findByUsername(username);
        if(user==null) {
            System.out.println("User Not Found");
            throw new UsernameNotFoundException("No User Found");
        }
        return user;
    }

}


JwtUtil.java

package com.expensesd.demo.config;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Component
public class JwtUtil {

    private String SECRET_KEY = "secret";

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }
    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }

    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername());
    }

    private String createToken(Map<String, Object> claims, String subject) {

        return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
    }

    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
}

JwtAuthenticationEntryyPoint.java

package com.expensesd.demo.config;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;


@Component
public class JwtAutenticationEntryyPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"UnAuthorized : Server");
        
    }

}

错误

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
[2m2021-08-11 12:43:21.452[0;39m [31mERROR[0;39m [35m14444[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.s.b.d.LoggingFailureAnalysisReporter  [0;39m [2m:[0;39m 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field authenticationManager in com.expenses.demo.controller.AuthenticateController required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'org.springframework.security.authentication.AuthenticationManager' in your configuration.

【问题讨论】:

  • 在我的配置中,我不需要定义authenticationManagerBean。相反,我只是让 spring 使用默认值 - 就像你似乎正在做的那样。我还在@EnableGlobalMethodSecurity 中启用了jsr250。不确定这是否应该有所作为? ??????

标签: java spring spring-boot spring-mvc jwt


【解决方案1】:

默认情况下使用 @Bean 而不提供名称,将导致创建的 bean 使用带注释的方法的名称命名,在您的情况下:

@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}

将继续创建一个名为 authenticationManagerBean 的 bean。相反,在您的注入点,您要求 Spring 注入一个名为:

@Autowired
private AuthenticationManager authenticationManager;

由于在 Spring 的上下文中不存在这样的 bean,因此注入失败并且您的应用程序崩溃。为了缓解这种情况:

  1. 使用带有@Bean 注释的方法名称(在您的情况下为authenticationManagerBean)命名注入的bean。
  2. 使用@Bean(name = "authenticationManager") 以使用注入点预期的名称命名您的bean。

【讨论】:

  • 我应该在哪里命名注入的bean意味着在哪个类中?
  • 要么在MySecurityConfigurations 处使用Bean(name = "authenticationManager"),要么在AuthenticateController 处将注入的值从authenticationManager 重命名为authenticationManagerBean
  • 你试过@Bean(BeanIds.AUTHENTICATION_MANAGER)吗? BeanIds 是 Spring Security 中的一个抽象类,仅用于保存常量。
【解决方案2】:

在 StackOverflow 上多次回答了类似的问题。例如here

尝试替换

@Override
@Bean
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(this.userDetailServiceImplementation).passwordEncoder(passwordEncoder());
}

@Override
@Bean
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.parentAuthenticationManager(authenticationManagerBean()).userDetailsService(this.userDetailServiceImplementation).passwordEncoder(passwordEncoder());
}

【讨论】:

  • 替换它也不能显示同样的错误。
猜你喜欢
  • 1970-01-01
  • 2015-06-30
  • 1970-01-01
  • 1970-01-01
  • 2019-01-05
  • 1970-01-01
  • 2014-12-15
  • 1970-01-01
  • 2020-10-04
相关资源
最近更新 更多