Oauth2.0 自定义响应值以及异常处理

这篇具有很好参考价值的文章主要介绍了Oauth2.0 自定义响应值以及异常处理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

自用的响应信息主体

import cn.hutool.http.ContentType;
import cn.hutool.http.HttpStatus;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import lombok.experimental.Accessors;

import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.nio.charset.Charset;


@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@ApiModel(value = "响应信息主体")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class R<T> implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final String SUCCESS = "SUCCESS";
    private static final String FAILED = "FAILED";

    @Getter
    @Setter
    @ApiModelProperty(value = "返回标记:成功标记=200,失败标记=500")
    private int code;

    @Getter
    @Setter
    @ApiModelProperty(value = "返回信息")
    private String msg;

    @Getter
    @Setter
    @ApiModelProperty(value = "数据")
    private T data;

    public static <T> R<T> ok() {
        return restResult(null, HttpStatus.HTTP_OK, SUCCESS, true);
    }

    public static <T> R<T> ok(T data) {
        return restResult(data, HttpStatus.HTTP_OK, SUCCESS, true);
    }

    public static <T> R<T> ok(T data, String msg) {
        return restResult(data, HttpStatus.HTTP_OK, msg, true);
    }

    public static <T> R<T> ok(ResultCode result) {
        return restResult(null, result.code, result.msg, true);
    }

    public static <T> R<T> fail() {
        return restResult(null, HttpStatus.HTTP_INTERNAL_ERROR, FAILED, false);
    }

    public static <T> R<T> fail(String msg) {
        return restResult(null, HttpStatus.HTTP_INTERNAL_ERROR, msg, false);
    }

    public static <T> R<T> fail(T data) {
        return restResult(data, HttpStatus.HTTP_INTERNAL_ERROR, FAILED, false);
    }

    public static <T> R<T> fail(ResultCode result) {
        return restResult(null, result.code, result.msg, false);
    }

    public static <T> R<T> fail(int code, String msg) {
        return restResult(null, code, msg, false);
    }

    private static <T> R<T> restResult(T data, int code, String msg, boolean success) {
        R<T> apiResult = new R<>();
        apiResult.setCode(code);
        apiResult.setData(data);
        apiResult.setMsg(msg);
        return apiResult;
    }

    public static void failRender(int code, String msg, HttpServletResponse response, int status) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            response.setContentType(ContentType.build(ContentType.JSON.getValue(), Charset.defaultCharset()));
            response.setStatus(status);
            response.getWriter().write(mapper.writeValueAsString(R.fail(code, msg)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void failRender(ResultCode resultCode, HttpServletResponse response, int status) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            response.setContentType(ContentType.build(ContentType.JSON.getValue(), Charset.defaultCharset()));
            response.setStatus(status);
            response.getWriter().write(mapper.writeValueAsString(R.fail(resultCode)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void cast(ResultCode resultCode) {
        throw new BusinessException(resultCode);
    }

    public static void cast(int code, String msg) {
        throw new BusinessException(code, msg);
    }
}

自定义无异常情况下请求 /oauth/token 获取 token 的响应格式

@Slf4j
@Aspect
@Component
public class CustomOAuthTokenAspect {

    @Around("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..))")
    public ResponseEntity response(ProceedingJoinPoint point) throws Throwable {
        Object proceed = point.proceed();
        ResponseEntity<OAuth2AccessToken> responseEntity = (ResponseEntity<OAuth2AccessToken>) proceed;
        return ResponseEntity.ok(R.ok(responseEntity.getBody()));
    }

}

处理 grant_type、username、password 错误的异常响应

  • 默认情况是使用 WebResponseExceptionTranslator接口的实现类 DefaultWebResponseExceptionTranslator对抛出的异常进行处理
  • 本文处理方法就是通过实现WebResponseExceptionTranslator接口来入手,来达到对异常信息的处理
  • 实现之后要记得将其添加到认证服务器核心配置 AuthorizationServerConfig 的端点配置 (AuthorizationServerEndpointsConfigurer.exceptionTranslator) 中,往下看会有写,不急
import cn.mowen.common.result.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InsufficientScopeException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.stereotype.Component;
import org.springframework.web.HttpRequestMethodNotSupportedException;

import java.io.IOException;

@Slf4j
@Component
public class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator {

    private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();

    @Override
    public ResponseEntity translate(Exception e) throws Exception {
        Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(e);
        Exception ase = (OAuth2Exception) this.throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain);
        //异常链中有OAuth2Exception异常
        if (ase != null) {
            return this.handleOAuth2Exception((OAuth2Exception) ase);
        }
        //身份验证相关异常
        ase = (AuthenticationException) this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
        if (ase != null) {
            return this.handleOAuth2Exception(new CustomWebResponseExceptionTranslator.UnauthorizedException(e.getMessage(), e));
        }
        //异常链中包含拒绝访问异常
        ase = (AccessDeniedException) this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
        if (ase instanceof AccessDeniedException) {
            return this.handleOAuth2Exception(new CustomWebResponseExceptionTranslator.ForbiddenException(ase.getMessage(), ase));
        }
        //异常链中包含Http方法请求异常
        ase = (HttpRequestMethodNotSupportedException) this.throwableAnalyzer.getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain);
        if (ase instanceof HttpRequestMethodNotSupportedException) {
            return this.handleOAuth2Exception(new CustomWebResponseExceptionTranslator.MethodNotAllowed(ase.getMessage(), ase));
        }

        return this.handleOAuth2Exception(new CustomWebResponseExceptionTranslator.ServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), e));
    }

    private ResponseEntity<OAuth2Exception> handleOAuth2Exception(OAuth2Exception e) throws IOException {
        int status = e.getHttpErrorCode();
        HttpHeaders headers = new HttpHeaders();
        headers.setCacheControl(CacheControl.noCache());
        headers.setPragma(CacheControl.noCache().getHeaderValue());
        if (status == HttpStatus.UNAUTHORIZED.value() || e instanceof InsufficientScopeException) {
            headers.set(HttpHeaders.WWW_AUTHENTICATE, String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary()));
        }
        ResponseEntity<OAuth2Exception> response = new ResponseEntity(R.fail(e.getMessage()), headers, HttpStatus.valueOf(status));
        return response;
    }

    private static class MethodNotAllowed extends OAuth2Exception {
        public MethodNotAllowed(String msg, Throwable t) {
            super(msg, t);
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "method_not_allowed";
        }

        @Override
        public int getHttpErrorCode() {
            return 405;
        }
    }

    private static class UnauthorizedException extends OAuth2Exception {
        public UnauthorizedException(String msg, Throwable t) {
            super(msg, t);
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "unauthorized";
        }

        @Override
        public int getHttpErrorCode() {
            return 401;
        }
    }

    private static class ServerErrorException extends OAuth2Exception {
        public ServerErrorException(String msg, Throwable t) {
            super(msg, t);
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "server_error";
        }

        @Override
        public int getHttpErrorCode() {
            return 500;
        }
    }

    private static class ForbiddenException extends OAuth2Exception {
        public ForbiddenException(String msg, Throwable t) {
            super(msg, t);
        }

        @Override
        public String getOAuth2ErrorCode() {
            return "access_denied";
        }

        @Override
        public int getHttpErrorCode() {
            return 403;
        }
    }
}

自定义客户端异常处理过滤器 CustomClientCredentialsTokenEndpointFilter

  • 自定义客户端异常处理过滤器: {“error”: “invalid_client”, “error_description”: “Bad client credentials”}
  • 通过配置到认证服务器 (AuthorizationServerConfig) 的 client 认证异常过滤器中 (client_id、client_secret 错误时会执行)
  • 配置的同时要设置 CustomAuthenticationEntryPoint 以此来格式化异常返回值
  • 注意:AuthenticationEntryPoint 没有实例,需要我们自己实现这个接口才能进行注入
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter;
import org.springframework.security.web.AuthenticationEntryPoint;

public class CustomClientCredentialsTokenEndpointFilter extends ClientCredentialsTokenEndpointFilter {
    private final AuthorizationServerSecurityConfigurer configurer;
    private AuthenticationEntryPoint authenticationEntryPoint;

    public CustomClientCredentialsTokenEndpointFilter(AuthorizationServerSecurityConfigurer configurer) {
        this.configurer = configurer;
    }

    @Override
    public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
        this.authenticationEntryPoint = authenticationEntryPoint;
    }

    @Override
    protected AuthenticationManager getAuthenticationManager() {
        return configurer.and().getSharedObject(AuthenticationManager.class);
    }

    @Override
    public void afterPropertiesSet() {
        setAuthenticationFailureHandler((request, response, exception) -> authenticationEntryPoint.commence(request, response, exception));
        setAuthenticationSuccessHandler((request, response, authentication) -> {
            // no-op - just allow filter chain to continue to token endpoint
        });
    }

}

两个公共异常处理类(未认证、未授权)

实现 AuthenticationEntryPoint 接口来处理认证异常的响应信息
  • 处理 Authentication 异常,如:token错误、过期
  • 配置一:配置到资源服务器核心配置中(ResourceServerConfig)对 token 进行校验
  • 配置二:配置到认证服务器核心配置中(AuthorizationServerConfig),当客户端异常(client_id、client_secret错误)时会执行
import cn.hutool.http.HttpStatus;
import cn.mowen.common.result.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        R.failRender(HttpStatus.HTTP_UNAUTHORIZED, exception.getMessage(), response, HttpStatus.HTTP_UNAUTHORIZED);
        log.error("Authentication异常: [{}], [{}], [{}]", request.getRequestURI(), exception.getMessage(), exception);
    }
}
实现 AccessDeniedHandler 接口来处理权限异常的响应信息
  • 处理权限异常的异常信息,只针对于资源服务器,认证服务器无需配置
  • 客户端权限异常 (resource_id),用户权限异常,自定义响应值
import cn.hutool.http.HttpStatus;
import cn.mowen.common.result.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception) throws IOException, ServletException {
        R.failRender(HttpStatus.HTTP_UNAUTHORIZED, exception.getMessage(), response, HttpStatus.HTTP_UNAUTHORIZED);
        log.error("AccessDenied异常: [{}], [{}], [{}]", exception.getMessage(), exception.getLocalizedMessage(), exception.toString());
    }
}

将以上定义的异常处理类添加到认证服务器核心配置 AuthorizationServerConfig 中

  • AuthorizationServerConfig 其他配置已省略,只需要找到对应方法进行追加就好,详细见 Oauth2.0 认证服务器搭建
@Configuration
@EnableAuthorizationServer
@AllArgsConstructor
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    /**
     * 用来配置令牌端点的安全约束, 密码校验方式等
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 自定义客户端异常处理过滤器: {"error": "invalid_client", "error_description": "Bad client credentials"}
        CustomClientCredentialsTokenEndpointFilter endpointFilter = new CustomClientCredentialsTokenEndpointFilter(security);
        endpointFilter.afterPropertiesSet();//初始化的时候执行
        endpointFilter.setAuthenticationEntryPoint(customAuthenticationEntryPoint);//格式化客户端异常的响应格式

        security
                //.allowFormAuthenticationForClients()
                .addTokenEndpointAuthenticationFilter(endpointFilter) //添加一个客户端认证之前的过滤器
        ;

        /*
         * allowFormAuthenticationForClients 的作用:
         * 允许表单认证(申请令牌), 而不仅仅是Basic Auth方式提交, 且url中有client_id和client_secret的会走 ClientCredentialsTokenEndpointFilter 来保护,
         * 也就是在 BasicAuthenticationFilter 之前添加 ClientCredentialsTokenEndpointFilter,使用 ClientDetailsService 来进行 client 端登录的验证。
         * 但是,在使用自定义的 CustomClientCredentialsTokenEndpointFilter 时,
         * 会导致 oauth2 仍然使用 allowFormAuthenticationForClients 中默认的 ClientCredentialsTokenEndpointFilter 进行过滤,致使我们的自定义 CustomClientCredentialsTokenEndpointFilter 不生效。
         * 因此在使用 CustomClientCredentialsTokenEndpointFilter 时,不再需要开启 allowFormAuthenticationForClients() 功能。
         */

    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.exceptionTranslator(customWebResponseExceptionTranslator)//自定义异常转换类(处理grant_type, username, password错误的异常)
        ;
    }

}

将那两个公共异常处理类配置到资源服务器的核心配置中 ResourceServerConfig

import cn.mowen.common.constant.OauthConstant;
import cn.mowen.common.constant.CommonWhiteConstant;
import cn.mowen.common.exception.oauth.CustomAuthenticationEntryPoint;
import cn.mowen.common.exception.oauth.CustomAccessDeniedHandler;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;

@Configuration
@EnableResourceServer
@AllArgsConstructor
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    private final TokenStore jwtTokenStore;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId(OauthConstant.OAUTH_RESOURCE_ID)
                .tokenStore(jwtTokenStore)
                .authenticationEntryPoint(new CustomAuthenticationEntryPoint())
                .accessDeniedHandler(new CustomAccessDeniedHandler())
                .stateless(true)
        ;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                //放行 url 在此配置
                .antMatchers(CommonWhiteConstant.white).permitAll()
                .antMatchers(white).permitAll()
                .anyRequest().authenticated()
        ;
    }

    //  白名单
    private static final String[] white = {
            "/test/**"
    };
    
}

Oauth2.0 系列文章

以下是同步到语雀的、可读性好一点,CSDN 继续看的点专栏就好。
Oauth2.0 核心篇
Oauth2.0 安全性(以微信授权登陆为例)
Oauth2.0 认证服务器搭建
Oauth2.0 添加验证码登陆方式
Oauth2.0 资源服务器搭建
Oauth2.0 自定义响应值以及异常处理
Oauth2.0 补充文章来源地址https://www.toymoban.com/news/detail-415383.html

到了这里,关于Oauth2.0 自定义响应值以及异常处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【OAuth2】OAuth2概述及使用GitHub登录第三方网站

    我们在浏览器上可以访问成百上千个网站,使用每个网站的服务一般都要先注册账号,那么我们为了更好地记忆,一般都会在多个网站使用相同的账号和密码进行注册。那么问题就来了,如果在你注册的网站中有某些个网站的系统设计不够严谨和安全,数据库的用户信息使用

    2024年01月19日
    浏览(56)
  • OAuth2 详细介绍!

    目录 一、文章介绍 二、OAth2 2.1 简介 2.2 OAuth2  授权总体流程 2.3 四种授权模式 1.授权码模式 2.简化模式 3.密码模式 4. 客户端模式 2.4 OAuth2 标准接口 2.5 GitHub 授权登录 1.创建 OAuth应用  2.项目开发 3.原理分析 3.原理总结 三、Spring Security OAuth2 3.1 授权、资源服务器 1.基于内存

    2024年01月16日
    浏览(77)
  • 了解oauth2.0

    开发授权 (OAuth)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片、视频、联系人列表等),而无需将用户名和密码提供给第三方应用。 OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。

    2024年02月09日
    浏览(34)
  • 【OAuth2系列】Spring Cloud Gateway 作为OAuth2 Client接入第三方单点登录代码实践

            在年初的时候我参与了一个项目,当时是很多家公司协同完成这个项目,其中一个公司专门负责登录这块的内容,需要我们的后端接入他们的单点登录(OAuth2 授权码模式),这块接入工作是由我来负责,我们的项目是微服务架构,经过网上各种查阅资料发现网关作为

    2024年02月04日
    浏览(63)
  • SpringSecurity+OAuth2.0

    OAuth(Open Authorization)是一个关于授权(authorization)的开放网络标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容。OAuth 在全世界得到广泛应用,目前的版本是 2.0 版。 简单

    2024年02月13日
    浏览(38)
  • OAuth2认证流程

    目录 什么是OAuth2 1. OAuth2认证流程 1、用户点击微信扫码 2、用户授权黑马网站访问用户信息 3、黑马程序员的网站获取到授权码 4、携带授权码请求微信认证服务器申请令牌 5、微信认证服务器向黑马程序员的网站响应令牌 6、黑马程序员网站请求微信资源服务器获取资源即用

    2024年02月16日
    浏览(50)
  • ARM异常处理(异常源的分类、异常的响应流程)

    目录 一、异常的概念 1、什么是异常? 2、处理异常时,处理器要考虑哪些问题? 二、ARM异常源 1、异常源的分类 2、异常模式 三、ARM异常响应 1、CPSR寄存器内容备份(自动执行) 2、修改CPSR的值(自动执行) (1) 修改模式 (2) 修改中断禁止位 (3) 修改状态位 3、保存返回地址(

    2023年04月15日
    浏览(35)
  • OAuth2 工作流程详解

    我们之前谈到了使用saml作为SSO单点登录认证。本文讲解oauth2.0协议,oauth2.0协议避免了客户端直接访问受保护资源 OAuth是一种安全的开放协议,用于在不相关的服务之间授权用户。换句话说,它使一个服务能够访问托管在其他服务上的资源,而无需共享用户凭证,如用户名和

    2024年02月08日
    浏览(40)
  • 17.Oauth2-微服务认证

    OAuth 2.0授权框架支持第三方支持访问有限的HTTP服务,通过在资源所有者和HTTP服务之间进行一个批准交互来代表资源者去访问这些资源,或者通过允许第三方应用程序以自己的名义获取访问权限。 为了方便理解,可以想象OAuth2.0就是在用户资源和第三方应用之间的一个中间层

    2024年02月10日
    浏览(40)
  • 18.Oauth2-微服务认证

    OAuth 2.0授权框架支持第三方支持访问有限的HTTP服务,通过在资源所有者和HTTP服务之间进行一个批准交互来代表资源者去访问这些资源,或者通过允许第三方应用程序以自己的名义获取访问权限。 为了方便理解,可以想象OAuth2.0就是在用户资源和第三方应用之间的一个中间层

    2024年02月10日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包