【问题标题】: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);
    }
}

【问题讨论】:

    标签: spring-boot authentication jwt


    【解决方案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 仍然为空。
    猜你喜欢
    • 2015-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-20
    • 1970-01-01
    • 1970-01-01
    • 2019-09-13
    • 2021-08-05
    相关资源
    最近更新 更多