如何使用身份验证管理器修复“错误凭据”错误?

Posted

技术标签:

【中文标题】如何使用身份验证管理器修复“错误凭据”错误?【英文标题】:How to fix "Bad credentials" error using authentication manager? 【发布时间】:2021-06-30 06:33:20 【问题描述】:

我正在做一个项目,您必须在其中注册一些用户并给他们一个角色(默认用户)。注册用户后,我添加了 jwt auth 并且能够获得 jwt 响应,但是在尝试对其执行一些过滤器后,代码开始失败。

此时我已经评论了过滤器实现方法以及我的 WebSecurityConfig。

假设我将在端点内收到用户名和密码(“authenticate/”)。

@RequestMapping(
            value = "authenticate", "authenticate/",
            method = RequestMethod.POST,
            consumes = 
                    MediaType.APPLICATION_FORM_URLENCODED_VALUE,
                    MediaType.MULTIPART_FORM_DATA_VALUE,
                    MediaType.APPLICATION_JSON_VALUE
            ,
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    public @ResponseBody AuthResponse login(AuthRequest authRequest) throws Exception
    
        return this.authService.authenticate(authRequest);
    

AuthRequest 是用户名和密码。

@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
public class AuthResponse

    private String username;
    private String password;

但错误是当我尝试使用 AuthenticationManager 对用户进行身份验证时。

AuthService 认证方法

public AuthResponse authenticate(AuthRequest authRequest) throws Exception
    
        //System.out.println(authRequest.getUsername() +"," + authRequest.getPassword() + "," + this.bCryptPasswordEncoder.encode(authRequest.getPassword()));
        try 
            this.authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
            );
         catch (BadCredentialsException e) 
            //Here is the error
            throw new Exception("Incorrect username or password", e);
        

        final UserDetails userDetails = usersDetailService.loadUserByUsername(authRequest.getUsername());
        final String jwt = jwtUtil.generateToken(userDetails);
        //this.logService.save(new Log(null, userDetails.getUsername(), jwt, null));
        System.out.println(jwt);
        return new AuthResponse(jwt);
    

邮递员控制台内部出错。

"timestamp": "2021-04-03T19:23:01.510+00:00",
    "status": 403,
    "error": "Forbidden",
    "trace": "java.lang.Exception: Incorrect username or password\n\tat com.cncipo.nl.auth.service.AuthService.authenticate2(AuthService.java:60)\n\tat com.cncipo.nl.controller.SessionRestController.auth(SessionRestController.java:85)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.lang.reflect.Method.invoke(Method.java:498)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:652)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:733)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327)\n\tat org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)\n\tat org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)\n\tat org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)\n\tat org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:105)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)\n\tat org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)\n\tat org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)\n\tat org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)\n\tat org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211)\n\tat org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183)\n\tat org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)\n\tat org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.lang.Thread.run(Thread.java:748)\nCaused by: org.springframework.security.authentication.BadCredentialsException: Bad credentials\n\tat org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:141)\n\tat org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182)\n\tat org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:201)\n\tat org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:518)\n\tat com.cncipo.nl.auth.service.AuthService.authenticate2(AuthService.java:56)\n\t... 86 more\n",
    "message": "Access Denied",
    "path": "/api/authenticate"

我将添加 WebSecurityConfig 和 JwtFilter 以防我遗漏了某些内容并导致错误。

JwtFilter

//@Component
public class JwtRequestFilter extends OncePerRequestFilter


    @Autowired
    private UsersDetailService usersDetailService;

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
    
        /* Get the header for auth */
        final String authHeader =  request.getHeader("Authorization");
        String username = null;
        String jwt = null;

        System.out.println("Flag of header it's null when I implemented" + authHeader);

        /* Extract jwt and username */
        if (authHeader != null && authHeader.startsWith("Bearer "))
        
            jwt = authHeader.substring(7);
            username = jwtUtil.extractUsername(jwt);
        

        /* Extract user details */
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null)
        
            UserDetails userDetails = this.usersDetailService.loadUserByUsername(username);

            if (jwtUtil.validateToken(jwt, userDetails))
            
                /* Create default new jwt auth token */
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            
        
        /* Handling the control to the next filter chain */
        filterChain.doFilter(request, response);
    

网络安全配置

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter


    @Autowired
    private UsersDetailService usersDetailService;

    //@Autowired
    //private JwtRequestFilter jwtRequestFilter;

    @Bean
    public BCryptPasswordEncoder passwordEncoder()
    
        return new BCryptPasswordEncoder();
    

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception
    
        auth.userDetailsService(usersDetailService);
    

    /* Bean of AuthManager due to it used to work in older version of spring */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception
    
        return super.authenticationManagerBean();
    

    @Override
    protected void configure(HttpSecurity http) throws Exception
    
        /* Disable security */
        http.csrf().disable();

        /* User info page requires login as admin and user role. If no login, redirect */
        http.authorizeRequests().antMatchers("users").access("hasAnyRole('admin', 'user')");

        /* Only for admin */
        http.authorizeRequests().antMatchers("logs", "admin").access("hasRole('admin')");

        /* The pages does not require login/auth */
        http.authorizeRequests().antMatchers("login", "logout", "/authenticate/, login/")
                .permitAll();//.anyRequest().authenticated();

        /* Using sessiong managment for intercept jwt auth . Spring won't create session, jwt will manage them*/
        //http.exceptionHandling().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

    

UsersDetailService,MyUserDetails 实现 UserDetails from import org.springframework.security.core.userdetails.UserDetails;

@Service
public class UsersDetailService implements UserDetailsService

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    
        User user = this.userRepository.getUserByUsername_user(username);

        if (user == null)  throw new UsernameNotFoundException("Could not find user"); 
        return new MyUserDetails(user);
    

【问题讨论】:

【参考方案1】:

尝试以下设置:

在您的安全配置中

 @Autowired
    private JwtFilterRequest jwtFilterRequest;

@Override
protected void configure(HttpSecurity http) throws Exception 
    http.csrf().disable().authorizeRequests()           
            .antMatchers("/forgetPassword").permitAll()
            .antMatchers("/registerUser").permitAll()
            .antMatchers("/login").permitAll()
            .anyRequest().authenticated()
            .and().exceptionHandling().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
            .and().formLogin().loginPage("/login").loginProcessingUrl("/login")
            .defaultSuccessUrl("/index.html").failureUrl("/login?error")
            .and().logout().logoutUrl("/logout");

    http.addFilterBefore(this.jwtFilterRequest, UsernamePasswordAuthenticationFilter.class);

过滤类JWT(JwtFilterRequest):

import cl.project.admin.security.jwt.JwtUtil;
import cl.project.admin.security.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;    
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class JwtFilterRequest extends OncePerRequestFilter 

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException 
        String authorizationHeader = request.getHeader("Authorization");

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) 
            String jwt = authorizationHeader.substring(7);
            String username = this.jwtUtil.extractUsername(jwt);

            if(username != null && SecurityContextHolder.getContext().getAuthentication() == null)
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

                if(this.jwtUtil.validateToken(jwt, userDetails))
                    UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                    SecurityContextHolder.getContext().setAuthentication(authToken);
                
            
        

        filterChain.doFilter(request, response);
    

您的自定义 UserDetailService:

import cl.project.admin.controllers.AttentionController;
import cl.project.admin.models.dao.IUserDao;
import cl.project.admin.models.entity.User;
import cl.project.admin.models.service.UserServiceImpl;
import org.apache.log4j.Logger;
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 org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;

@Service
public class CustomUserDetailsService implements UserDetailsService 
    Logger logger = Logger.getLogger(CustomUserDetailsService.class);

    @Autowired
    private UserServiceImpl userService;

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
        User user = userService.findByUsername(username);

        if(user == null) 
            this.logger.error("Error al ingresar: " + username);
            throw new UsernameNotFoundException(username);
        


        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>());
    

JWT 实用程序

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

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

@Service
public class JwtUtil 
    
    @Value("$keyJWTSpringSecurity")
    private static final String KEY = "passwordInYourApplicationProperties";


    public String generateToken(UserDetails userDetail)
        return Jwts.builder().setSubject(userDetail.getUsername()).setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 1000*60*60*5))
                .signWith(SignatureAlgorithm.HS256, KEY).compact();
    

    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(KEY).parseClaimsJws(token).getBody();
    

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

    public String generateToken(String username) 
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, username);
    

    public String generateTokenInfinity(String username) 
        Map<String, Object> claims = new HashMap<>();
        return createTokenInfinity(claims, username);
    

    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 * 5))
                .signWith(SignatureAlgorithm.HS256, KEY).compact();
    

    private String createTokenInfinity(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 * 800))
                .signWith(SignatureAlgorithm.HS256, KEY).compact();
    

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

【讨论】:

非常感谢亚历克斯!我只有一个疑问。如何仅针对管理员角色限制某些端点(用户、管理员等)?如果离开这个 permitAll().anyRequest().authenticated() 我得到一个 403 未经授权的错误,我的 authorizationHeader 仍然为空。

以上是关于如何使用身份验证管理器修复“错误凭据”错误?的主要内容,如果未能解决你的问题,请参考以下文章

基本身份验证错误凭据的预期行为

身份验证失败 - grails spring security中的错误凭据

如何修复阻止注册用户登录的护照本地身份验证错误

通过 Spring Security 的 Active Directory 身份验证返回由 LDAP 引起的有效用户的错误凭据:错误代码 49

使用 Next-Auth 进行身份验证时如何修复内部服务器错误

如何修复来自 Apollo 客户端的格式错误的身份验证标头错误