Java Spring - Authentication always returning a BadCredentialsException - java

I'm very confused with this issue. When I print username I get the correct data I sent, however, credentials for some reasons returns null, unless I hardcode the data (this is not the case if when I do this same call in the controller.
But not only that, even when I hardcode the data with .findByPhoneNumber("123456789"), and successfully find a Credentials object in my database, the controller is still throwing an exception.
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
CredentialsRepository credentialsRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Credentials credentials = credentialsRepository.findByPhoneNumber(username);
System.out.println(username);
System.out.println(credentials);
if (credentials == null) {
throw new UsernameNotFoundException("Incorrect credentials");
}
User userDetails = new User(credentials.getPhoneNumber(), credentials.getOtp(),
new ArrayList<>());
return userDetails;
}
}
try {
System.out.println("Here1");
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(phoneNumber, otp)
);
}
catch (BadCredentialsException e) {
System.out.println("Here2");
throw new Exception("Incorrect username or password", e);
}
In other words, I am always getting a BadCredentialsException, regardless of the successful return or not of loadUserByUsername.

Related

Spring security authentication manager exception

If I understand good, I have custom Authentication Manager class, and in there I check if someone in api pass correct credentials, but I wonder why it didn't throw exception while I passed empty username and password.
#Component
public class AuthManager implements AuthenticationManager {
private final DetailsService detailsService;
private final Logger logger = LoggerFactory.getLogger(AuthManager.class);
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
logger.info("credentials: " + authentication.getCredentials());
logger.info("principals: " + authentication.getPrincipal());
if (authentication.getCredentials() == null || authentication.getPrincipal() == null) {
throw new BadCredentialsException("Credentials are wrong");
}
UserDetails user = loadUser(authentication);
return new UsernamePasswordAuthenticationToken(user.getUsername(), null, user.getAuthorities());
}
private UserDetails loadUser(Authentication auth) {
return detailsService.loadUserByUsername(auth.getPrincipal().toString());
}
That's filter
#Component
public class UsernamePasswordJsonFilter extends UsernamePasswordAuthenticationFilter {
private final ObjectMapper objectMapper;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public UsernamePasswordJsonFilter(ObjectMapper objectMapper, AuthManager manager,
AuthSuccessHandler success, AuthFailureHandler failure) {
this.objectMapper = objectMapper;
setAuthenticationSuccessHandler(success);
setAuthenticationFailureHandler(failure);
setAuthenticationManager(manager);
setFilterProcessesUrl("/login");
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
LoginDTO authenticationRequest = objectMapper.readValue(request.getInputStream(), LoginDTO.class);
Authentication auth = new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(),
authenticationRequest.getPassword());
logger.info("UsernamePasswordJsonFilter");
return getAuthenticationManager().authenticate(auth);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
If I pass correct username and password it works, but I just wonder why it didn't throw exception when credentials are empty, also there is no exception thrown in console, in case someone asks for it
When you use
LoginDTO authenticationRequest = objectMapper.readValue(request.getInputStream(), LoginDTO.class);
It return LoginDTO with login = "" and password = "". It is not a null.
"" is not the same as null. If u want to be null in LoginDTO, request should not have username or password field at all
if (authentication.getCredentials() == null || authentication.getPrincipal() == null)
Here you also to check to empty string. Let's say "".equals(authentication.getCredentials()) or authentication.getCredentials().isEmpty()
same with authentication.getPrincipal()

Do I need a separate JWT Filter for Multiple Logins?

The User login is working well but I want to add a Customer Module to the project. I know that I need to write a custom UserDetails class to get the customer Username but I want to ask if I need to write another Custom JWT filter for the Customer Login validation. Presently this is the Filter class that I have for User Login. I have added a username and password field to the Customer entity.
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private UserAccountService myUserDetailsService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
String authorities = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority)
.collect(Collectors.joining());
System.out.println("Authorities granted : " + authorities);
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
else {
System.out.println("Not Valid Token");
}
}
chain.doFilter(request, response);
}
}
As you can see the Filter is using the custom UserDetails to verify the username . How do I add the Customer userdetails service to the filter ? This is my first multiple login project please be lenient with me.
Differentiate between user and customer while logging. Accordingly, call the different service to get user details. More can be found here.
Spring Security user authentication against customers and employee
How do I add the Customer userdetails service to the filter?: inject it as you did with UserAccountService. If you do this way, you're using 1 filter (and of course, this filter is in 1 SecurityFilterChain), you could basically implement your filter like: trying to validate your user by myUserDetailsService and if it's not successful, continue with myCustomerDetailsService.
For multiple login project. The second way you could do is using 2 SecurityFilterChain. UserJwtFilter for 1 SecurityFilterChain and CustomJwtFilter for 1 SecurityFilterChain for example. People usually do this way for different login mechanisms Basic, OAuth2, SAML2. E.g:
Basic Authentication:
#Configuration
#Order(2)
public class BasicAuthenticationFilterChain extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/login", "/logout")
.and()
OAuth2 Authentication:
#Configuration
#Order(3)
public class OAuth2AuthenticationFilterChain extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/oauth")
.and()
In this case when a request with "/login" it'll be directed to BasicAuthenticationFilterChain, and a request with "/oauth" will go to OAuth2AuthenticationFilterChain. About Order: the lower is the higher priority and once the request's processed with a SecurityFilterChain, it won't go to another SecurityFilterChain. You can implement your project this way.
Conclusion: There are a lot of ways you can implement your idea with spring security, it depends on your choice.
it looks to me like you already did.
#Autowired
private UserAccountService myUserDetailsService;
But I would suggest using a Constructor instead of #Autowired. Spring will fill in the constructor parameters just the same. This could be very slim when you use the lombok library as well.
Using a constructor also makes mocking this a bit easier for testing.
Updated as discussed in the comments:
#Log //another lombok thing
#RequiredArgsConstructor
#Component
public class JwtRequestFilter extends Filter{
private final JwtTokenUtil jwtTokenUtil;
private final UserAccountService myUserDetailsService;
private final CustomerAccountService myCustomerDetailsService;
private static final String AUTH_HEADER = "authorization";
#Override
protected void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
String tokenHeader = ((HttpServletRequest) request).getHeader(AUTH_HEADER);
if(hasValue(tokenHeader) && tokenHeader.toLowerCase().startsWith("bearer ")){
jwtToken = requestTokenHeader.substring(7);
String username;
String jwtToken;
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
if (uSecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = myUserDetailsService.loadUserByUsername(username);
if(isNull(userDetails)){
userDetails = myCustomerDetailsService.loadCustomerByUsername(username);
}
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
var token = createSecurityToken(userDetails);
SecurityContextHolder.getContext().setAuthentication(token);
} else {
throw new RuntimeException("Not a Valid Token.");
}
} else {
log.info("Authorization already present");
}
} catch (IllegalArgumentException e) {
throw new("Unable to get JWT Token",e);
} catch (ExpiredJwtException e) {
throw new("JWT Token has expired",e);
}
} else {
throw new RuntimeException("No valid authorization header found.");
}
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken createSecurityToken(UserDetails userDetails){
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
log.info("Authorities granted : {}", userDetails.getAuthorities());
token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
return token;
}
}

Spring Security with JWT token is loading spring UserDetails object on every request

I am learning spring security with JWT token and spring boot. I have implemented it properly and it is working fine. But I have one doubt in how JwtRequestFilter works. I have gone through couple of websites to understand spring security with spring boot and found same thing. So let me go to main doubt.
I am adding JwtRequestFilter file below.
JwtRequestFilter.java
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
// JWT Token is in the form "Bearer token". Remove Bearer word and get
// only the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
// Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// This below line is calling on every request
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
// if token is valid configure Spring Security to manually set
// authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the
// Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
As highlighed to validate token we have to provide spring UserDetails object and we are getting spring UserDetails object from jwtUserDetailsService. So every request this filter will call then token verification will perform and we have to call jwtUserDetailsService on every request.
My doubt is inside my jwtUserDetailsService I am adding couple of validation and adding user privileges. So on every request below steps are repeated in jwtUserDetailsService.
Get user using username from DB.
Get user role
Get user privileges from DB.
Assign privileges to userDetails.
JwtUserDetailsService.java
#Service("jwtUserDetailsService")
#Transactional
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private IUserService service;
#Autowired
private MessageSource messages;
#Autowired
private RoleRepository roleRepository;
#Override
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
if (user == null) {
return new org.springframework.security.core.userdetails.User(
" ", " ", true, true, true, true,
getAuthorities(Arrays.asList(
roleRepository.findByName("ROLE_USER"))));
}
return new org.springframework.security.core.userdetails.User(
user.getEmail(), user.getPassword(), user.isEnabled(), true, true,
true, getAuthorities(user.getRoles()));
}
private Collection<? extends GrantedAuthority> getAuthorities(
Collection<Role> roles) {
return getGrantedAuthorities(getPrivileges(roles));
}
private List<String> getPrivileges(Collection<Role> roles) {
List<String> privileges = new ArrayList<>();
List<Privilege> collection = new ArrayList<>();
for (Role role : roles) {
collection.addAll(role.getPrivileges());
}
for (Privilege item : collection) {
privileges.add(item.getName());
}
return privileges;
}
private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (String privilege : privileges) {
authorities.add(new SimpleGrantedAuthority(privilege));
}
return authorities;
}
}
So on every request these queries are executing. Is there any better way of doing this? Because once I am adding user privileges in spring UserDetails object why we need to do that again on every request. Or those have scope of request only. I have worked on spring mvc and once we add privileges in spring UserDetails object it will be there until I am hitting logout means It will be there in spring security context until we remove it. Will it be same in spring boot? If I am adding role and privileges details once in spring UserDetails object why we need to add it again?
So every request this filter will call then token verification will
perform and we have to call jwtUserDetailsService on every request.
This can't be correct since you have a condition if (SecurityContextHolder.getContext().getAuthentication() == null).
So the first time the token was validated, you query your user details service, fetch all grants and set them to the Security context (you are already doing it: SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);).
Furthermore, with the JWT auth you usually don't even need to access any user details service since all grants ideally should be contained in the token itself. So the only thing you need to do is validating token's signature.
Once an user logs in his authentication is established, so you don't need to do db call again, after login in every request user should be checked for authorization only with the roles being set in the token during authentication, you need to validate the token is not tampered in every request
instead of creating userdetails by loading user detail from db call
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
you could also encode the user's username and roles inside JWT claims
and create the UserDetails object by parsing those claims from the JWT.

How to replace exception handling structure

I trying to implement spring security + jwt.I done log-in and log-out methods, jwt filter,provider and web config is configured. So the main problem is my controller and how to return error messages to user, for example if user typed wrong password/username or user account is banned etc.
I got a structure built on exception handling, looks terible.
controller
#PostMapping("/log-in")
public ResponseEntity logIn(#RequestBody UserDto userDto) {
log.info("[LOG-IN] user with username " + userDto.getUsername());
try {
HashMap<String, String> response = userService.logIn(userDto);
return ResponseEntity.ok(response);
} catch (UserStatusException ex) {
return ResponseEntity.badRequest().body("Account is Pending");
} catch (UsernameNotFoundException ex) {
return ResponseEntity.badRequest().body("Could not find account!");
} catch (AuthenticationException ex) {
log.error("Wrong username or password!");
return ResponseEntity.badRequest().body("Wrong username or password!");
}
}
service
#Override
public HashMap<String, String> logIn(UserDto userDto)throws AuthenticationException, UserStatusException{
User user = findByUsername(userDto.getUsername());
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userDto.getUsername(), userDto.getPassword())); //login
checkUserStatus(user); //check if user pending or banned
user.setUserStatus(UserStatus.ACTIVE);
String token = jwtTokenProvider.createToken(user.getUsername(), user.getUserRoles());
HashMap<String, String> response = new HashMap<>();
response.put("token", token);
response.put("username", user.getUsername());
userRepository.save(user);
return response;
}
#Override
public User findByUsername(String username)throws UsernameNotFoundException {
log.info("[UserService, findByUsername]");
User user = userRepository.findByUsername(username);
if(user == null){
log.error("User not found with {} username: ", username);
throw new UsernameNotFoundException("User not found!");
}
log.info("User {} successfully loaded ",username);
return user;
}
#Override
public void checkUserStatus(User user)throws UserStatusException {
if (user.getUserStatus().equals(UserStatus.BANNED)
|| user.getUserStatus().equals(UserStatus.PENDING)) {
throw new UserStatusException("Not confirmed");
}
}
Is there any other way to replace this structure?
You should use a ControllerAdvice (see a tutorial here).
It's a special class that look like this
#ControllerAdvice
public class ControllerAdvice {
#ExceptionHandler(PersonNotFoundException.class)
public ResponseEntity <VndErrors > notFoundException(final PersonNotFoundException e) {
return error(e, HttpStatus.NOT_FOUND, e.getId().toString());
}
}
It will allow you to bind a specific return code and response to each exception you need to handle, and it will automatically catch all exception returned by your controller. It's also a good way to handle all exceptions at the same place instead of over each exceptions...
I'm not sure about it, but I think you even can bind it to a specific mapping of your API for more granularity.
Hope this help! Have fun!
You could add the repose status directly to your custom exception class:
#ResponseStatus(HttpStatus.BAD_REQUEST)
public class UsernameNotFoundException extends RuntimeException {
public UsernameNotFoundException(String message) {
super(message);
}
public UsernameNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
In this way you don't need anymore to catch them in the controller and add the message and the status in ResponseEntity.

Please ensure that at least one realm can authenticate these tokens

So I have set up my shiro to have two Realms. A Username and Password Realm, using the standard UsernamePasswordToken. I have also set up a Custom Bearer Authentication Token that works off a token passed in from the user.
If i just use my passwordValidatorRealm it works find, if no user is found throws unknown account, if password doesn’t match throws incorrect credentials, perfect. But as soon as i put in my tokenValidatorRealm it throws a
org.apache.shiro.authc.AuthenticationException: Authentication token of type [class org.apache.shiro.authc.UsernamePasswordToken] could not be authenticated by any configured realms.
In this instance my tokenValidatorRealm returns null as no token was provided, so it moves on to the passwordValidatorRealm and just breaks.
Any ideas why introducing a second Realm will cause my working passwordValidatorRealm to break?
Have tried with different authentication strategies, and no luck there.
Using shiro 1.2.2
EDIT
I have two implementations, one for password and one for token
Password:
public class PasswordAuthorizingRealm extends AuthenticatingRealm {
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
if (authenticationToken instanceof UsernamePasswordToken) {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
String username = usernamePasswordToken.getUsername();
char[] password = usernamePasswordToken.getPassword();
if (username == null) {
throw new AccountException("Null usernames are not allowed by this realm!");
}
//Null password is invalid
if (password == null) {
throw new AccountException("Null passwords are not allowed by this realm!");
}
UserService userService = new UserServiceImpl();
User user = userService.getUserByUsername(username);
if (user == null) {
throw new UnknownAccountException("Could not authenticate with given credentials");
}
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, user.getPassword(), "passwordValidatorRealm");
return simpleAuthenticationInfo;
} else {
return null;
}
}
}
and Bearer Token
public class TokenAuthorizingRealm extends AuthorizingRealm {
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
if (authenticationToken instanceof BearerAuthenticationToken) {
BearerAuthenticationToken bearerAuthenticationToken = (BearerAuthenticationToken) authenticationToken;
String username = "" + bearerAuthenticationToken.getPrincipal();
User user = userService.getUserByUsername(username);
//User with such username has not found
if (user == null) {
throw new UnknownAccountException("Could not authenticate with given credentials");
}
BearerAuthenticationInfo bearerAuthenticationInfo = new BearerAuthenticationInfo(user);
return bearerAuthenticationInfo;
}
}
Shiro config
[main]
hashService = org.apache.shiro.crypto.hash.DefaultHashService
hashService.hashIterations = 500000
hashService.hashAlgorithmName = SHA-256
hashService.generatePublicSalt = true
hashService.privateSalt = ****
passwordService = org.apache.shiro.authc.credential.DefaultPasswordService
passwordService.hashService = $hashService
passwordMatcher = org.apache.shiro.authc.credential.PasswordMatcher
passwordMatcher.passwordService = $passwordService
authc = my.BearerTokenAuthenticatingFilter
tokenValidatorRealm = my.TokenAuthorizingRealm
passwordValidatorRealm = my.PasswordAuthorizingRealm
passwordValidatorRealm.credentialsMatcher = $passwordMatcher
securityManager.realms = $tokenValidatorRealm,$passwordValidatorRealm
These have been stripped out a bit, removed logging and other unnecessary code
The BearerTokenAuthenticatingFilter, just basically checks if a token has been supplied in the header if has
private void loginUser(ServletRequest request, ServletResponse response) throws Exception {
BearerAuthenticationToken token = (BearerAuthenticationToken) createToken(request, response);
if (token == null) {
String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken "
+ "must be created in order to execute a login attempt.";
throw new IllegalStateException(msg);
}
try {
Subject subject = getSubject(request, response);
subject.login(token);
onLoginSuccess(token, subject, request, response);
} catch (AuthenticationException e) {
HttpServletResponse httpResponse = WebUtils.toHttp(response);
httpResponse.sendRedirect("login");
}
}
BearerAuthenticationInfo class
public class BearerAuthenticationInfo implements AuthenticationInfo {
private final PrincipalCollection principalCollection;
private final User user;
public BearerAuthenticationInfo(User user) {
this.user = user;
this.principalCollection = buildPrincipalCollection(user);
}
public PrincipalCollection getPrincipals() {
return principalCollection;
}
public Object getCredentials() {
return user.getUsername();
}
private PrincipalCollection buildPrincipalCollection(User user) {
Collection<String> principals = new ArrayList<String>();
principals.add(user.getUsername());
return new SimplePrincipalCollection(principals, "tokenValidatorRealm");
}
}
Looks like it is expected behavior.
If you look at the javadoc for ModularRealmAuthenticator:
* #throws AuthenticationException if the user could not be authenticated or the user is denied authentication
* for the given principal and credentials.
*/
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
If you are having problems with the exception, you might need to change the code that calls the authentication to expect this exception.
Left for other searches:
You might have a missing supports method in your TokenAuthorizingRealm class.
Something like
#Override
public boolean supports(AuthenticationToken token) {
return token instanceof BearerAuthenticationToken;
}
should be present.
This discussion help me solve a similar problem. I wanted to authenticate a user by the application itself, not using any Shiro default implementation. To do that we must subclass AuthenticatingRealm, override doGetAuthenticationInfo and declare this realm as the validation one.
public class PasswordAuthorizingRealm extends AuthenticatingRealm {
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
In Shiro.ini:
passwordValidatorRealm = my.PasswordAuthorizingRealm

Categories