简体   繁体   中英

No message field with Unauthorized Exceptions in Spring Boot

I have made a custom exception which should give the message to client when raised. However, that does not seem to be working whenever error code is 401 ie UNAUTHORIZED. I tried changing the status code to something else, and message showed up.

Note - I have already set the flag server.error.include-message=always in application.properties


BadCredentialsException.java

@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
public class BadCredentialsException extends RuntimeException{
    // Runtime exception just needs this, I guess :/
    private static final long serialVersionUID = 1;

    public BadCredentialsException(String message){
        super(message);
    }

}


Here's how I have tried raising the exception.
 public ResponseEntity<Boolean> loginUser(String username, String password){ // validating username User user = myUserRepository.findByUsername(username). orElseThrow(() -> new ResourceNotFoundException("No username: " + username + " found. Please enter a correct username;")). // validating password if(,new BCryptPasswordEncoder().matches(password. user;getPassword())){ throw new BadCredentialsException("Incorrect Password. Please enter correct password to login;"); } return ResponseEntity.ok(true); }

Note - message is correctly being displayed in the terminal though. Just not showing up on the client.

UPDATE 1 - I have made this particular endpoint to be accessible by everyone, using permitAll(). In postman, when I select select "no auth" and call this endpoint with expected exception, exception does not give the message unless, I give correct basic auth credentials (any role). I have absolutely no clue why this is happening.

UPDATE 2 - Adding SecurityConfiguration code.

SecurityConfiguration.java

 @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(). authorizeRequests(). antMatchers(HttpMethod.POST, "/api/v2/user/login/**").permitAll(). antMatchers(HttpMethod.POST, "/api/v2/user/", "/api/v2/user", "/api/v2/user/change-role/**").hasAuthority("ROOT"). antMatchers(HttpMethod.GET, "/api/v2/user/", "/api/v2/user").hasAuthority("ROOT"). antMatchers(HttpMethod.POST, "/api/v1/customers/", "/api/v1/customers").hasAnyAuthority("ADMIN", "ROOT"). antMatchers(HttpMethod.GET, "/api/v1/customers/", "/api/v1/customers").hasAnyAuthority("EMPLOYEE", "ADMIN", "ROOT"). anyRequest(). authenticated(). and(). httpBasic(); }

Actually, there are multiple ways to do that. First, as @SergVasylchak said in the comments, you can use ControllerAdvice . So the approach is like below:

@ControllerAdvice
public class GlobalExceptionHandler {

 @ExceptionHandler(BadCredentialsException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.UNAUTHORIZED)
    private Message handleMessageAuth(BadCredentialsException e, HttpServletRequest request) {
        Message message = new Message();
        message.setTimestamp(getTimeStamp());
        message.setError(HttpStatus.UNAUTHORIZED.getReasonPhrase());
        message.setStatus(HttpStatus.UNAUTHORIZED.value());
        message.setMessage(e.getMessage());
        message.setPath(String.valueOf(request.getRequestURI()));
        return message;
    }
}

Message is your custom pojo.

public class Message {
    private String timestamp;
    private int status;
    private String error;
    private String message;
    private String path;
// getters & setters
}

Another solution is that implement AuthenticationEntryPoint .

What is AuthenticationEntryPoint?

It is an interface implemented by ExceptionTranslationFilter, basically a filter which is the first point of entry for Spring Security. It is the entry point to check if a user is authenticated and logs the person in or throws exception (unauthorized). Usually, the class can be used like that in simple applications but when using Spring security in REST, JWT etc one will have to extend it to provide better Spring Security filter chain management. AuthenticationEntryPoint is used in Spring Web Security to configure an application to perform certain actions whenever an unauthenticated client tries to access private resources.

Look at this .

AuthenticationEntryPoint is used to send an HTTP response that requests credentials from a client. Sometimes a client will proactively include credentials such as a username/password to request a resource. In these cases, Spring Security does not need to provide an HTTP response that requests credentials from the client since they are already included. In other cases, a client will make an unauthenticated request to a resource that they are not authorized to access. In this case, an implementation of AuthenticationEntryPoint is used to request credentials from the client. The AuthenticationEntryPoint implementation might perform a redirect to a log in page, respond with a WWW-Authenticate header, etc.

For example, if you have a JWT authentication the approach is like below.

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
// if you want to return 401 status, this is enough.

// if you want to customize your response you can make it as below.
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.getOutputStream().println("{ \"error\": \"" + authException.getMessage() + "\" }"); 
    }
}

Now it's time to config the AuthenticationEntryPoint in SecurityConfig.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private JwtAuthenticationEntryPoint authenticationEntryPoint;


@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable().
   .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
   .and()
    authorizeRequests().        
    antMatchers(HttpMethod.POST, "/api/v2/user/login/**").permitAll().
    antMatchers(HttpMethod.POST, "/api/v2/user/", "/api/v2/user", "/api/v2/user/change-role/**").hasAuthority("ROOT").
    antMatchers(HttpMethod.GET, "/api/v2/user/", "/api/v2/user").hasAuthority("ROOT").
    antMatchers(HttpMethod.POST, "/api/v1/customers/", "/api/v1/customers").hasAnyAuthority("ADMIN", "ROOT").
    antMatchers(HttpMethod.GET, "/api/v1/customers/", "/api/v1/customers").hasAnyAuthority("EMPLOYEE", "ADMIN", "ROOT").
    anyRequest().
    authenticated().
    and().
    httpBasic();
}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM