본문에 제출 된 사용자 / 암호가 유효한 경우 토큰을 생성하기 위해 Spring에서 처리 된 경로를 구축하려고합니다. 해당 시나리오에서만 토큰으로 응답합니다.
문제는 정확히 DB에 저장된대로 올바른 사용자 이름과 암호를 게시하고 있지만 "잘못된 자격 증명"오류가 계속 발생한다는 것입니다. 이것이 컨트롤러입니다.
@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest authenticationRequest) throws Exception {
try {
LOGGER.info("Received a request to generate a token for user: "+authenticationRequest.getUsername()+"/"+authenticationRequest.getPassword());
final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
System.out.println(userDetails);
// Logs: org.springframework.security.core.userdetails.User@7ee29d27: Username: thisWasTheGoodUserName; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: Admin
// And also logs the hibernate query:
// select
// employee0_.id as id1_0_,
// employee0_.employee_name as employee_name2_0_,
// employee0_.pwd as pwd3_0_,
// employee0_.user_name as user_nam4_0_
// from
// employees employee0_
// where
// employee0_.user_name=?
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, authenticationRequest.getPassword(), userDetails.getAuthorities());
// This step gets executed
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
// And the same query to the DB is logged, one that I can run on dbeaver manually and get results from:
// select
// employee0_.id as id1_0_,
// employee0_.employee_name as employee_name2_0_,
// employee0_.pwd as pwd3_0_,
// employee0_.user_name as user_nam4_0_
// from
// employees employee0_
// where
// employee0_.user_name=?
// And throws exception in this authenticate
final String token = jwtTokenUtil.generateToken(userDetails);
TokenSucess tokenSuccessResponse = new TokenSucess(true, new JwtResponse(token));
LOGGER.info("Obtained token with success ");
return new ResponseEntity<TokenSucess>(tokenSuccessResponse, HttpStatus.OK);
} catch (Exception e) {
TokenError tokenErrorResp = new TokenError(false, "Error generating token.");
LOGGER.error("Error generating a token. Details: "+ e);
return new ResponseEntity<TokenError>(tokenErrorResp, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
이것은 내가 서비스에서 사용하는 방법입니다.
@Service
public class JwtUserDetailsService implements UserDetailsService {
@Autowired
private EmployeeRepository employeeRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Employee emp = employeeRepository.findByUserName(username);
if (emp == null ) {
throw new UsernameNotFoundException("Employee not found with username: " + username);
}
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("Admin"));
return new org.springframework.security.core.userdetails.User(emp.getUserName(), emp.getPassword(), authorities);
}
}
Security 구성 :
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private UserDetailsService jwtUserDetailsService;
@Autowired
private JwtRequestFilter jwtRequestFilter;
private static final Logger LOGGER = LoggerFactory.getLogger(WebSecurityConfig.class);
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
try {
httpSecurity.csrf().disable()
// don't authenticate this particular request
.authorizeRequests().antMatchers("/authenticate").permitAll().
// all other requests need to be authenticated
anyRequest().authenticated().and().
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
} catch (Exception e) {
LOGGER.error("Error handling http security configs. Details: "+e.getMessage());
}
}
}
그리고 이것은 컬의 예입니다.
curl --location --request POST 'http://127.0.0.1:<port>/authenticate' \
--header 'Content-Type: application/json' \
--data-raw '{
"username": "thisWasTheGoodUserName",
"password": "123454321"
}'
이미 DB에서 다른 시나리오로 시도했습니다.
1. thisWasTheGoodUserName/123454321
2. thisWasTheGoodUserName/$2y$12$Mj0PRHipe14Wgm5c/GOuO.RyhjhuwRwoQYUnK8LcgsvHzQ4weYHGm (bcrypted 123454321)
여러 "단계 진입"을 사용 /Users/<user>/.m2/repository/org/springframework/security/spring-security-core/5.0.3.RELEASE/spring-security-core-5.0.3.RELEASE.jar!/org/springframework/security/authentication/dao/DaoAuthenticationProvider.class
하여 암호가 동일한 문자열 인 경우에도 다음 함수에서 문제가 발생하는 것을 발견했습니다 .
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
this.logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
} else {
String presentedPassword = authentication.getCredentials().toString();
// presentedPassword is exactly the same as the one in userDetails.getPassword() but the matcher returns False...
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
this.logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
}
내가 뭘 잘못하고 다음 예외를 일으킬 수 있는지 아십니까?
ERROR c.r.t.s.c.JwtAuthenticationController - Error generating a token. Details: org.springframework.security.authentication.BadCredentialsException: Bad credentials
참고 :이 모든 원인이되는 문제를 발견했습니다. DB에 저장된 비밀번호가 온라인 bcrypt 생성기를 사용하여 암호화되었습니다 ... 값이 동일한 문자열이지만 생성 된 bcrypt 인코딩 된 비밀번호와 일치하지 않습니다.