Spring Security是自带一套异常处理系统的,自定义返回数据有两种方式:
方式一(较麻烦不推荐):
- 自定义异常,继承OAuth2Exception
@JsonSerialize(using = ExtendOAuth2ExceptionSerializer.class)
public class ExtendOAuth2Exception extends OAuth2Exception {
public ExtendOAuth2Exception(String msg) {
super(msg);
}
public ExtendOAuth2Exception(String msg, Throwable t) {
super(msg, t);
}
}
public class GrantTypeException extends ExtendOAuth2Exception{
public GrantTypeException(String msg, Throwable t) {
super(msg, t);
}
public GrantTypeException(String msg) {
super(msg);
}
@Override
public String getOAuth2ErrorCode() {
return "grant_type_unsupported---不支持当前授权类型!!!";
}
@Override
public int getHttpErrorCode() {
return 400;
}
}
- 在校验的代码处(Granter或Provider处,一般是在Provider处做校验)抛出异常
@Data
@Component("SmsCodeAuthenticationProvider")
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
@Resource(name = "MobileUserDetailsService")
private MobileUserDetailsService userDetailsService;
@Autowired
private CheckCodeClient checkCodeClient;
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
String mobile = (String) authenticationToken.getPrincipal();
String code = (String) authenticationToken.getCredentials();
UserDetails userDetails =userDetailsService.loadUserByMobile(mobile);
if (null==userDetails){
throw new InvalidInformationException("该电话号码为注册",null);
}
Boolean b = checkCodeClient.verifySmsCheckCode(mobile, code);
// 验证码比对
if (!b) {
throw new InvalidInformationException("验证码错误",null);
}
SmsCodeAuthenticationToken result = new SmsCodeAuthenticationToken(userDetails,userDetails.getPassword(),userDetails.getAuthorities());
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
}
}
- 编写translator,重写translate方法,在translate方法中可以对自定义的异常进行处理
@Slf4j
public class OAuthServerWebResponseExceptionTranslator implements WebResponseExceptionTranslator {
private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
@Override
public ResponseEntity translate(Exception e) throws Exception {
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e);
Exception ase = null;// 异常栈获取 OAuth2Exception 异常
//自定义认证方式异常
ase=(UnsupportedGrantTypeException) throwableAnalyzer
.getFirstThrowableOfType(UnsupportedGrantTypeException.class, causeChain);
if (ase != null) {
return handleOAuth2Exception(new GrantTypeException(e.getMessage(), e));
}
//自定义未认证异常
ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class,
causeChain);
if (ase != null) {
return handleOAuth2Exception(new ForbiddenException(ase.getMessage(), ase));
}
//自定义方法不允许访问异常
ase = (HttpRequestMethodNotSupportedException) throwableAnalyzer
.getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain);
if (ase != null) {
return handleOAuth2Exception(new MethodNotAllowedException(ase.getMessage(), ase));
}
//自定义认证失败异常
ase = (InvalidInformationException) throwableAnalyzer
.getFirstThrowableOfType(InvalidInformationException.class, causeChain);
if (ase != null) {
return handleOAuth2Exception(new InvalidInformationException(ase.getMessage(), ase));
}
ase = (InvalidGrantException) throwableAnalyzer
.getFirstThrowableOfType(InvalidGrantException.class, causeChain);
if (ase != null) {
return handleOAuth2Exception(new InvalidGrantException(ase.getMessage(), ase));
}
//自定义其他异常
ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain);
if (ase != null) {
return handleOAuth2Exception((OAuth2Exception) ase);
}
// 不包含上述异常则服务器内部错误
return handleOAuth2Exception(new ServerErrorException(HttpStatus.OK.getReasonPhrase(), e));
}
// private ResponseEntity<OAuth2Exception> handleOAuth2Exception(OAuth2Exception e) {
//
// int status = e.getHttpErrorCode();
// HttpHeaders headers = new HttpHeaders();
// headers.set("Cache-Control", "no-store");
// headers.set("Pragma", "no-cache");
// if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) {
// headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary()));
// }
// log.error("内部产生异常", e);
//
// return new ResponseEntity<>(e, headers,
// HttpStatus.valueOf(status));
// }
private ResponseEntity<HttpResponseEntity> handleOAuth2Exception(OAuth2Exception e) {
log.error("内部产生异常", e);
int status = e.getHttpErrorCode();
HttpHeaders headers = new HttpHeaders();
headers.set("Cache-Control", "no-store");
headers.set("Pragma", "no-cache");
if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) {
headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary()));
}
HttpResponseEntity responseEntity=new HttpResponseEntity();
if(e instanceof InvalidInformationException){
responseEntity.setCode(Code.LOGIN_ERR);
responseEntity.setMessage(e.getMessage());
}else {
responseEntity.setCode(Code.PROJECT_BUSINESS_ERROR);
responseEntity.setMessage(e.getMessage());
}
return new ResponseEntity<>(responseEntity, headers,
HttpStatus.valueOf(status));
}
}
- 使用该translator
@Override // 令牌端点的安全约束配置
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenServices(tokenService())//令牌管理服务
.authenticationManager(authenticationManager)//认证管理器
.allowedTokenEndpointRequestMethods(HttpMethod.POST)
.accessTokenConverter(accessTokenConverter)
.tokenStore(tokenStore)
.setClientDetailsService(clientDetails());//客户端信息服务
//获取原有默认的授权者(那四种模式)
ArrayList<TokenGranter> tokenGranters = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
//添加扩展的验证码模式授权者
tokenGranters.add(new CaptchaTokenGranter(endpoints.getTokenServices(),endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), authenticationManager,checkCodeClient));
//添加扩展的邮箱密码模式授权者
tokenGranters.add(new EmailPwdGranter(authenticationManager,endpoints.getTokenServices(),endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
//添加扩展的短信验证码模式授权者
tokenGranters.add(new SmsCodeTokenGranter(endpoints.getTokenServices(),endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), authenticationManager));
//添加自定义的用户名密码模式授权者
tokenGranters.add(new MyUsernamePasswordGranter(authenticationManager,endpoints.getTokenServices(),endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
CompositeTokenGranter compositeTokenGranter=new CompositeTokenGranter(tokenGranters);
endpoints.tokenGranter(compositeTokenGranter); //配置授权者
endpoints.exceptionTranslator(new OAuthServerWebResponseExceptionTranslator());//配置异常转换器
}
方式二(推荐):
绕过Spring Security的异常处理机制,自己处理:
- 定义controller
@RestController
@RequestMapping("/oauth")
public class AuthController {
@Autowired
private TokenEndpoint tokenEndpoint;
/**
* Oauth2登录认证
*/
@RequestMapping(value = "/token", method = RequestMethod.POST)
public HttpResponseEntity postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
Oauth2TokenDto oauth2TokenDto = Oauth2TokenDto.builder()
.token(oAuth2AccessToken.getValue())
.refreshToken(oAuth2AccessToken.getRefreshToken().getValue())
.expiresIn(oAuth2AccessToken.getExpiresIn())
.tokenHead("Bearer ").build();
return new HttpResponseEntity(Code.LOGIN_OK,oauth2TokenDto,"登录成功!!!");
}
}
- 定义异常处理器
@ControllerAdvice
public class Oauth2ExceptionHandler {
@ResponseBody
@ExceptionHandler(value = OAuth2Exception.class)
public HttpResponseEntity handleOauth2(OAuth2Exception e) {
e.printStackTrace();
return new HttpResponseEntity(Code.LOGIN_ERR, e,e.getMessage());
}
}