我们已经在Angular + Spring Boot + JPA项目中实现了Spring Security 但是登录时遇到以下异常-从邮递员运行时进行身份验证:-
`trace": "org.springframework.security.authentication.BadCredentialsException: Bad credentials\r\n\tat org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:93)\r\n\tat org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:166)\r\n\tat org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:175)\r\n\tat org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:200)\r\n\tat org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:503)\r\n\tat com.jwt.security.controller.AuthenticationController.login(AuthenticationController.java:40)\r\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\r\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)\r\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)\r\n\tat java.lang.reflect.Method.invoke(Unknown Source)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:90
表示网址:http://localhost:8080/login 和身体 : { “电子邮件”:“ abcd@test.com”, “密码”:“ 123” }
代码如下
SecurityConfiguration.java是配置类
package com.jwt.security;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
import com.jwt.security.filter.AuthenticationTokenFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration<jwtAuthenticationEntryPoint> extends WebSecurityConfigurerAdapter{
@Autowired private UserDetailsService userDetailsService;
@Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint ;
@Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder( passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public AuthenticationTokenFilter authenticationTokenFilterBean( ) {
return new AuthenticationTokenFilter();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/registration").permitAll()
.antMatchers("/login").permitAll()
.antMatchers(HttpMethod.OPTIONS ,"/**").permitAll()
.anyRequest().authenticated();
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
httpSecurity.headers().cacheControl();
httpSecurity.headers().httpStrictTransportSecurity().includeSubDomains(true).maxAgeInSeconds(31536000);
}
}
JwtAuthenticationEntryPoint.java
package com.jwt.security;
import java.io.IOException;
import java.io.Serializable;
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 JwtAuthenticationEntryPoint implements AuthenticationEntryPoint , Serializable {
/**
*
*/
private static final long serialVersionUID = 3293526539341241958L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Unauthorized");
}
}
JwtTokenUtil.java代码在下面
package com.jwt.security;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@Component
public class JwtTokenUtil implements Serializable {
static final String CLAIM_KEY_USERNAME = "sub";
static final String CLAIM_KEY_AUDIENCE = "audience";
static final String CLAIM_KEY_CREATED = "created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String getUsernameFromToken(String authToken) {
String username = null;
try {
final Claims claims = getClaimsFromToken(authToken);
username = claims.getSubject();
} catch (Exception e) {
// TODO Auto-generated catch block
username = null;
}
return username;
}
private Claims getClaimsFromToken(String authToken) {
// TODO Auto-generated method stub
Claims claims = null;
try {
claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(authToken).getBody();
} catch (Exception e) {
// TODO Auto-generated catch block
claims = null;
}
return claims;
}
public boolean validateToken(String authToken, UserDetails userDetails) {
// TODO Auto-generated method stub
JwtUser user = (JwtUser) userDetails;
final String username = getUsernameFromToken(authToken);
return (username.equals(user.getUsername()) && !isTokenExpired(authToken));
}
private boolean isTokenExpired(String authToken) {
final Date expiration = getExpirationDateFromToken(authToken);
return expiration.before(new Date());
}
private Date getExpirationDateFromToken(String authToken) {
// TODO Auto-generated method stub
Date expiration = null;
final Claims claims = getClaimsFromToken(authToken);
if (claims != null) {
expiration = claims.getExpiration();
} else {
expiration = null;
}
return expiration;
}
public String generateToken(JwtUser userDetails) {
Map<String,Object> claims = new HashMap<String,Object>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
public String generateToken(Map<String , Object> claims ) {
return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate()).signWith(SignatureAlgorithm.HS512, secret).compact();
}
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
}
JwtUser实现UserDetails
package com.jwt.security;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.jwt.security.domain.User;
public class JwtUser implements UserDetails {
private final Long id;
private final String username;
private final String password;
private final User user;
private final boolean enabled;
private final Collection <? extends GrantedAuthority > authorities;
public JwtUser(Long id, String username, String password, User user, boolean enabled,
Collection<? extends GrantedAuthority> authorities) {
super();
this.id = id;
this.username = username;
this.password = password;
this.user = user;
this.enabled = enabled;
this.authorities = authorities;
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getUsername() {
// TODO Auto-generated method stub
return null;
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return enabled;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return authorities;
}
@JsonIgnore
public Long getId() {
return id;
}
public User getUser() {
return user;
}
@Override
public String toString() {
return "JwtUser [id=" + id + ", username=" + username + ", password=" + password + ", user=" + user
+ ", enabled=" + enabled + ", authorities=" + authorities + "]";
}
}
JwtUserDetailsServiceImpl是服务
/**
*
*/
package com.jwt.security;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
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.jwt.security.domain.User;
import com.jwt.security.repository.UserRepository;
@Service
public class JwtUserDetailsServiceImpl implements UserDetailsService {
//@Autowired private UserRepository userRepository;
@Autowired UserRepository userRepository;
/* private static final AtomicLong counter = new AtomicLong();
private static List<User> users;
static {
users = populateDummyUsers();
}
private static List<User> populateDummyUsers() {
List<User> users = new ArrayList<User>();
return users;
}
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//User userdb = null;
User userdb = userRepository.findByEmailIgnoreCase(username);
UserDetails details = null;
/* for (User user : users) {
if (user.getEmail() == username) {
userdb = user;
}
}*/
if(username == null) {
throw new UsernameNotFoundException(String.format("No User found with username '%s'.", username));
}else {
details= JwtuserFactory.create(userdb);
System.out.println("details" + details);
return details;
}
}
}
JwtuserFactory.java如下
package com.jwt.security;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import com.jwt.security.domain.User;
public class JwtuserFactory {
public static JwtUser create(User user) {
// TODO Auto-generated method stub
return new JwtUser(user.getId(), user.getEmail(), user.getPassword(), user, user.isEnabled(), maptoGrantedAuthorities(new ArrayList<String>(Arrays.asList("ROLE_" + user.getRole()))));
}
private static List<GrantedAuthority> maptoGrantedAuthorities(List<String> authorities) {
// TODO Auto-generated method stub
return authorities.stream().map(Authority -> new SimpleGrantedAuthority(Authority)).collect(Collectors.toList());
}
}
AuthenticationController代码如下
package com.jwt.security.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.jwt.security.JwtTokenUtil;
import com.jwt.security.JwtUser;
import com.jwt.security.domain.User;
import com.jwt.security.domain.UserDTO;
import com.jwt.security.exception.UnauthorizedException;
@RestController
public class AuthenticationController {
@Value("${jwt.header}")
private String tokenHeader;
@Autowired private AuthenticationManager authenticationManager;
@Autowired private JwtTokenUtil jwtTokenUtil;
@PostMapping(value="/login")
public ResponseEntity<UserDTO> login(@RequestBody User user, HttpServletRequest request , HttpServletResponse response) {
try {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
//System.out.println("matches ::" + encoder.matches("123", user.getPassword()));
System.out.println("User entered password" + user.getPassword() );
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
final JwtUser userDetails = (JwtUser)authentication.getPrincipal();
SecurityContextHolder.getContext().setAuthentication(authentication);
final String token = jwtTokenUtil.generateToken(userDetails);
response.setHeader("Token", token);
return new ResponseEntity<UserDTO>(new UserDTO(userDetails.getUser(), token) , HttpStatus.OK);
}catch(UnauthorizedException ex) {
ex.printStackTrace();
throw new UnauthorizedException(ex.getMessage());
}
}
}
AuthenticationTokenFilter为:-
package com.jwt.security.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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.web.filter.OncePerRequestFilter;
import com.jwt.security.JwtTokenUtil;
public class AuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value("${jwt.header}")
private String tokenHeader;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// TODO Auto-generated method stub
String authToken = request.getHeader(this.tokenHeader);
if (authToken != null && authToken.length() > 7) {
authToken = authToken.substring(7);
}
String username = jwtTokenUtil.getUsernameFromToken(authToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
boolean isValid = jwtTokenUtil.validateToken(authToken, userDetails);
if (isValid) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
PasswordUtil如下:
package com.jwt.security.util;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class PasswordUtil {
static BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
public static String getPasswordHash(String password) {
return encoder.encode(password);
}
}
在数据库中,密码以加密方式存储。即使传递正确的参数,它也会产生BadCredentials错误
请提出建议并提供解决方案,以使身份验证可用于登录
答案 0 :(得分:0)
您的JwtUser
getPassword()
和getUsername()
方法都返回null
,但是在您当前的设置中,getPassword()
导致了异常。
您将获得一个BadCredentialsException
,因为默认的DaoAuthenticationProvider
将使用提供的密码和使用UserDetailsService
检索到的密码进行检查,并且返回的密码为null
。 / p>