You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过单端点调用在Spring Authorization Server获取授权码

无UI获取OAuth2授权码的实现方案

核心逻辑

授权码模式本来需要用户在浏览器里完成登录、确认授权,要跳过UI,本质就是模拟用户手动填表单登录的过程——直接给授权服务器发请求提交账号密码,同时带上授权相关参数,让服务器自动走完流程,直接返回带授权码的跳转地址。

具体操作步骤

1. 先明确两个关键端点

你得先搞清楚授权服务器的两个核心地址:

  • 授权请求端点:默认是 /oauth2/authorize
  • 表单登录提交端点:Spring Security 默认是 /login

2. 用Postman实现的步骤

第一步:获取CSRF令牌(若开启CSRF保护)

先给授权端点发GET请求,带上完整授权参数:

GET http://localhost:8080/oauth2/authorize?response_type=code&client_id=你的客户端ID&redirect_uri=已注册的回调地址&scope=你的权限范围

注意:redirect_uri必须是你在授权服务器配置中已注册的合法地址,否则会触发错误。

这个请求会返回登录页面的HTML,你在响应内容里搜索_csrf字段,提取它的value值——这是Spring Security强制要求的表单提交凭证,必须携带。

第二步:模拟登录请求

构造POST请求到/login端点,填写以下表单参数:

  • username: 你的测试账号
  • password: 你的测试密码
  • _csrf: 上一步拿到的CSRF令牌值
  • redirect: 把第一步的完整授权请求URL直接填进去(就是带response_type、client_id的那个URL)

发送请求后,服务器会自动完成登录和授权流程,返回3xx重定向响应。查看响应头里的Location字段,其中的code参数就是你要的授权码。

测试环境简化技巧:临时关闭CSRF

如果只是测试用,不想折腾CSRF令牌,可以在授权服务器的Security配置里临时关闭CSRF(生产环境绝对禁止这么做):

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        .formLogin(form -> form.permitAll())
        .csrf(csrf -> csrf.disable()); // 仅限测试环境使用
    return http.build();
}

关闭后就无需提取CSRF令牌,直接发送登录请求即可。

3. 用Java HTTP客户端实现的步骤

以Spring的RestTemplate为例,流程和Postman一致:

第一步:提取CSRF令牌(开启CSRF时)

RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));

// 请求授权端点,获取登录页面HTML
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> loginPageResp = restTemplate.exchange(
    "http://localhost:8080/oauth2/authorize?response_type=code&client_id=你的客户端ID&redirect_uri=已注册回调地址&scope=你的权限范围",
    HttpMethod.GET,
    entity,
    String.class
);

// 用正则提取_csrf的value值
Pattern csrfPattern = Pattern.compile("<input name=\"_csrf\" type=\"hidden\" value=\"(.*?)\"/>");
Matcher matcher = csrfPattern.matcher(loginPageResp.getBody());
String csrfToken = matcher.find() ? matcher.group(1) : "";

第二步:提交登录并获取授权码

MultiValueMap<String, String> loginParams = new LinkedMultiValueMap<>();
loginParams.add("username", "test-user");
loginParams.add("password", "test-pass");
loginParams.add("_csrf", csrfToken);
loginParams.add("redirect", "http://localhost:8080/oauth2/authorize?response_type=code&client_id=你的客户端ID&redirect_uri=已注册回调地址&scope=你的权限范围");

HttpEntity<MultiValueMap<String, String>> loginEntity = new HttpEntity<>(loginParams, headers);
// 让RestTemplate自动跟踪重定向
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        // 忽略3xx重定向错误,允许自动跳转
        if (!HttpStatus.Series.REDIRECTION.equals(response.getStatusCode().series())) {
            super.handleError(response);
        }
    }
});

// 发送登录请求
ResponseEntity<Void> authResp = restTemplate.exchange(
    "http://localhost:8080/login",
    HttpMethod.POST,
    loginEntity,
    Void.class
);

// 从Location头中提取授权码
String redirectUrl = authResp.getHeaders().getLocation().toString();
String authCode = redirectUrl.split("code=")[1].split("&")[0];
System.out.println("拿到的授权码:" + authCode);

重要提醒

  • 生产环境绝对不能关闭CSRF保护,必须严格处理CSRF令牌,避免安全风险。
  • 务必保证redirect_uri是在授权服务器的RegisteredClient配置中注册过的地址,否则服务器会拒绝请求。
  • 如果你的授权服务器自定义了登录端点或表单参数名,需对应调整请求中的参数。

内容的提问来源于stack exchange,提问作者Arthur Klezovich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 16:13:24