开发者社区> 问答> 正文

Spring boot 前后台分离项目 怎么处理spring security 抛出的异常报错 

最近在开发一个项目 前后台分离的 使用 spring boot + spring security + jwt 实现用户登录权限控制等操作。但是 在用户登录的时候,怎么处理spring  security  抛出的异常呢?使用了@RestControllerAdvice 和@ExceptionHandler 不能处理Spring Security抛出的异常,如 UsernameNotFoundException等,我想要友好的给前端返回提示信息  如,用户名不存在之类的。 贴上我的代码: JWT 验证类 : 重写了spring security UsernamaPasswordAuthenticationFilter

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

private AuthenticationManager authenticationManager;

private RedisServiceImpl redisService;

private AppConfig appConfig;

public JWTAuthenticationFilter(AuthenticationManager authenticationManager, RedisServiceImpl redisService, AppConfig appConfig) {
    this.authenticationManager = authenticationManager;
    this.redisService = redisService;
    this.appConfig = appConfig;
}

/**
 * @param req
 * @param res
 * @return
 * @throws AuthenticationException
 * @// TODO: 2018/4/12 接受并解析用户凭证
 */
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException {
    try {
        AuthEntity creds = new ObjectMapper()
                .readValue(req.getInputStream(), AuthEntity.class);

        //验证码校验
        if (appConfig.getCaptchaEnabled()) { //如果开启了验证码登录校验功能
            if (StringUtils.isBlank(creds.getCaptcha())) {
                logger.error("验证码为空");
                throw new WelendException(StatusCode.CAPTCHA_EMPTY);
            }
            if (!redisService.exists(appConfig.getCaptchaKey())) {
                logger.error("验证码已失效");
                throw new WelendException(StatusCode.CAPTCHA_OVERDUE);
            }
            String captcha = (String) redisService.get(appConfig.getCaptchaKey());
            if (!creds.getCaptcha().equals(captcha)) {
                logger.error("验证码不正确");
                throw new WelendException(StatusCode.CAPTCHA_ERROR);
            }
        }
        return authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        creds.getUsername(),
                        creds.getPassword(),
                        new ArrayList<>())
        );
    } catch (IOException e) {
        logger.error("Client's variables can't be parsed by com.fasterxml.jackson.core.JsonParse");
        throw new WelendException(StatusCode.SERVER_ERROR);
    }

}

}

验证用户名 密码:
public class CustomAuthenticationProvider implements AuthenticationProvider {

private UserDetailsServiceImpl userDetailsService;

private BCryptPasswordEncoder bCryptPasswordEncoder;

public CustomAuthenticationProvider(UserDetailsServiceImpl userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
    this.userDetailsService = userDetailsService;
    this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    // 获取认证的用户名 & 密码
    String name = authentication.getName();
    String password = authentication.getCredentials().toString();
    // 认证逻辑
    JWTUserDetails userDetails = userDetailsService.loadUserByUsername(name);
    if (null != userDetails) {
        Boolean verifyPwd = bCryptPasswordEncoder.matches(password,userDetails.getLoginPwd());
        if (verifyPwd) {
            // 生成令牌 这里令牌里面存入了:userDetails,password,authorities(权限列表)
            Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
            return auth;
        } else {
            throw new BadCredentialsException("username or password wrong!");
        }
    } else {
        throw new UsernameNotFoundException("can not find this account");
    }
}

/**
 * 是否可以提供输入类型的认证服务
 * @param authentication
 * @return
 */
@Override
public boolean supports(Class<?> authentication) {
    return authentication.equals(UsernamePasswordAuthenticationToken.class);
}

}

全局异常处理
@RestControllerAdvice public class GlobalExceptionHandler { private Logger logger = LoggerFactory.getLogger(getClass());

/**
 * @param request
 * @param exception
 * @return
 * @throws Exception
 * @// TODO: 2018/4/25 参数未通过验证异常
 */
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Object MethodArgumentNotValidHandler(HttpServletRequest request, MethodArgumentNotValidException exception) throws Exception {
    //按需重新封装需要返回的错误信息
    //List<StatusCode> invalidArguments = new ArrayList<>();
    //解析原错误信息,封装后返回,此处返回非法的字段名称,原始值,错误信息
    ResultObject resultMsg = ResultObject.dataMsg(exception.getBindingResult().getFieldError().getDefaultMessage(), StatusCode.VARIABLE_ERROR);
    return resultMsg;
}

/**
 * @param request
 * @param exception
 * @return
 * @throws Exception
 * @// TODO: 2018/4/25 无法解析参数异常
 */
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public Object HttpMessageNotReadableHandler(HttpServletRequest request, HttpMessageNotReadableException exception) throws Exception {
    logger.info(exception.getMessage());
    ResultObject resultMsg = ResultObject.dataMsg("参数无法正常解析", StatusCode.VARIABLE_ERROR);
    return resultMsg;
}

/**
 * @param exception
 * @return
 * @throws Exception
 * @// TODO: 2018/4/25 处理token 过期异常
 */
@ExceptionHandler(value = ExpiredJwtException.class)
public Object ExpiredJwtExceptionHandler(ExpiredJwtException exception) throws Exception {
    logger.info(exception.getMessage());
    ResultObject resultMsg = ResultObject.dataMsg("登录已过期!", StatusCode.FORBIDDEN);
    return resultMsg;
}

/**
 * @param request
 * @param exception
 * @return
 * @throws Exception
 * @// TODO: 2018/4/25 方法访问权限不足异常
 */
@ExceptionHandler(value = AccessDeniedException.class)
public Object AccessDeniedExceptionHandler(AccessDeniedException exception) throws Exception {
    logger.info(exception.getMessage());
    ResultObject resultMsg = ResultObject.dataMsg("权限不足!", StatusCode.FORBIDDEN);
    return resultMsg;
}

@ExceptionHandler(value = NoHandlerFoundException.class)
public Object NoHandlerFoundExceptionHandler(NoHandlerFoundException exception) throws Exception {
    logger.info(exception.getMessage());
    return ResultObject.dataMsg("链接不存在", StatusCode.NOT_FOUND);
}
/**
 * 处理自定义异常
 */
@ExceptionHandler(value = WelendException.class)
public Object WelendExceptionHandler(WelendException e) {
    ResultObject r = new ResultObject();
    r.setStatus(String.valueOf(e.getCode()));
    r.setMessage(e.getMessage());
    return r;
}

@ExceptionHandler(value = AuthenticationException.class)
public Object AuthenticationExceptionHandler(AuthenticationException e) {
    return ResultObject.dataMsg(e.getLocalizedMessage(),StatusCode.FORBIDDEN);
}

@ExceptionHandler(value = DuplicateKeyException.class)
public Object DuplicateKeyExceptionHandler(DuplicateKeyException e) throws Exception {
    logger.error(e.getMessage(), e);
    return ResultObject.codeMsg(StatusCode.EXISTED);
}

@ExceptionHandler(value = BadCredentialsException.class)
public Object BadCredentialsExceptionHandler(BadCredentialsException e) throws Exception {
    logger.error(e.getMessage(), e);
    return ResultObject.codeMsg(StatusCode.AUTH_ERROR);
}

@ExceptionHandler(value = Exception.class)
public Object ExceptionHandler(Exception e) throws Exception {
    logger.error(e.getMessage(), e);
    return ResultObject.codeMsg(StatusCode.FAILED);
}

}

登录时输入错误的用户名 控制台直接打印信息了, 并没有经过ExceptionHandler 处理。 如上所示,我想在全局异常类中 处理spring security抛出异常, 以便返回友好的提示信息。有什么解决办法么?

展开
收起
kun坤 2020-06-03 10:39:32 1639 0
1 条回答
写回答
取消 提交回答
  • 这个需要重写其他方法的######异常处理,json输出,code, msg######首先,你要明白,spring security的工作原理,他是基于filter,也就是说他抛出的异常的时候,请求还没有到达我们的servlet,用你的那种方式使用dispatchServlet的异常捕获机制当然捕获不到异常######但可以在filter里返回json到前端吧

    2020-06-03 10:39:40
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
云栖社区特邀专家徐雷Java Spring Boot开发实战系列课程(第20讲):经典面试题与阿里等名企内部招聘求职面试技巧 立即下载
微服务架构模式与原理Spring Cloud开发实战 立即下载
阿里特邀专家徐雷Java Spring Boot开发实战系列课程(第18讲):制作Java Docker镜像与推送到DockerHub和阿里云Docker仓库 立即下载