概述
在认证与授权的技术栈中,http 协议承载了客户端与资源服务器之间几乎所有的信任传递。
无论是经典的 session-cookie 模式,还是基于 token 的 oauth2 流程,敏感信息、身份凭证、令牌都嵌在 http 请求与响应的特定位置上。
本文从一次受保护的 rest 请求入手,拆解 http 请求的解剖结构,并结合 spring security 的实际配置,理解 http basic 认证头、csrf 保护、请求体格式等内容如何在工程中落地。
纲要
- http 请求的核心结构:起始行、请求头、实体头
- 使用 intellij http client 发送 api 请求
- 携带认证头:http basic 认证的原生写法与 base64 规则
- 请求参数传递的三种形式:查询参数、路径参数、json body
content-type实体头的作用- spring security 上下文中的 csrf 保护与临时绕过
- 完整可运行的 spring boot 示例代码(安全配置与 controller)
http 请求的解剖结构
一个标准的 http 请求由纯文本构成,主要分为三大部分:
- 起始行:包含请求方法、uri 和协议版本,例如
get /api/demo http/1.1 - 请求头:一组键值对,用于描述客户端环境、期望的响应格式、身份信息等。根据用途可细分为通用头、请求头、实体头,但它们在格式上完全一致
- 实体体:对于
post、put、patch等方法,请求可携带数据体,例如 json、表单字段等
浏览器开发者工具的 network 面板中将信息归类为 request headers 和 response headers,其中前者便对应上述请求头部分。实体头(如 content-type)在传递数据时用于告知服务端数据的编码格式,它本质上也属于请求头的一部分。
使用 http client 快速调试
如果已安装 intellij 的 rest client 插件(通常名为 “http client”),任何扩展名为 .http 或 .rest 的文件都会被识别为请求文件。使用 ### 分隔不同的请求。
一个最简单的 get 请求可以写成:
### 获取公共资源 get http://localhost:8080/api/hello http/1.1
当我们的资源受 spring security 保护时,直接访问会收到 401 unauthorized。为了让请求通过认证,需要加入认证头。
携带认证头:http basic 认证
http basic 认证是最直接的身份传递方式,它使用的请求头格式为:
authorization: basic base64(username:password)
标准要求将 用户名:密码 拼接后使用 base64 编码。http client 提供了简化写法,可以直接写入原始用户名与密码,工具会自动完成 base64 编码:
### 携带 basic 认证头 get http://localhost:8080/api/hello http/1.1 authorization: basic user password
发送后服务端将验证凭证,若通过则返回 200 ok 及响应体。这种将认证信息附加在请求头中的模式,与 oauth2 的 bearer 令牌传递方式在结构上如出一辙,只是认证方案标识和令牌内容不同。
post 请求与 csrf 的首次遭遇
将方法改为 post,并携带相同的认证头,有时会发现请求直接被 403 forbidden 拒绝,日志中出现 invalid csrf token。这是因为 spring security 默认开启 csrf 防护,对所有状态改变的请求(如 post、put、delete)均要求携带一个由服务端生成的 _csrf token。
如果是在前后端分离、移动端或无状态的 token 认证架构中,通常需要关闭 csrf 保护。通过在安全配置中调用 csrf().disable() 即可:
http
.csrf().disable()
.authorizerequests()
.anyrequest().authenticated()
.and()
.httpbasic();重启应用后,post 请求将不再因缺少 csrf token 而被拒绝。
请求参数的三种传递形式
查询参数(query string)
查询参数通过 url 中的 ?key=value&key2=value2 传递,多用于 get 和某些 post 场景:
### 携带查询参数 post http://localhost:8080/api/greet?name=wangwu http/1.1 authorization: basic user password
controller 侧使用 @requestparam 接收:
@postmapping("/greet")
public string greet(@requestparam string name) {
return "hello, " + name;
}路径参数(path variable)
参数直接作为 url 路径的一部分,使用 @pathvariable 绑定:
@getmapping("/users/{name}")
public string getuser(@pathvariable string name) {
return "user: " + name;
}
对应请求:
### 路径参数 get http://localhost:8080/users/zhangsan http/1.1 authorization: basic user password
请求体携带 json 数据
当需要传递结构化数据时,通常将 json 放在请求体中,并必须通过 content-type 头告知服务端格式:
### 发送 json 数据
post http://localhost:8080/api/user/info http/1.1
authorization: basic user password
content-type: application/json
{
"name": "lisi",
"gender": "male",
"idnumber": "123456199001011234"
}服务端定义一个 dto 类并使用 @requestbody 接收:
public class userinfo {
private string name;
private string gender;
private string idnumber;
// getters and setters
}
@postmapping("/api/user/info")
public string receiveuser(@requestbody userinfo user) {
return "received: " + user.getname();
}content-type: application/json 是一个典型的实体头,它告诉服务端“请求体的内容是 json”。如果缺失或填写错误,会收到 415 unsupported media type。
完整可运行代码示例
下面给出一个可直接运行的 spring boot 项目结构,包含安全配置与示例 controller。
src/main/java/com/example/demo/
├── demoapplication.java
├── config
│ └── securityconfig.java
└── controller
└── democontroller.java主启动类:
package com.example.demo;
import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
@springbootapplication
public class demoapplication {
public static void main(string[] args) {
springapplication.run(demoapplication.class, args);
}
}安全配置(关闭 csrf,启用 http basic,设定内存用户):
package com.example.demo.config;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.security.config.annotation.web.builders.httpsecurity;
import org.springframework.security.config.annotation.web.configuration.enablewebsecurity;
import org.springframework.security.core.userdetails.user;
import org.springframework.security.core.userdetails.userdetails;
import org.springframework.security.core.userdetails.userdetailsservice;
import org.springframework.security.provisioning.inmemoryuserdetailsmanager;
import org.springframework.security.web.securityfilterchain;
@configuration
@enablewebsecurity
public class securityconfig {
@bean
public securityfilterchain filterchain(httpsecurity http) throws exception {
http
.csrf().disable()
.authorizerequests()
.anyrequest().authenticated()
.and()
.httpbasic();
return http.build();
}
@bean
public userdetailsservice userdetailsservice() {
userdetails user = user.withdefaultpasswordencoder()
.username("user")
.password("password")
.roles("user")
.build();
return new inmemoryuserdetailsmanager(user);
}
}控制器(涵盖 get、post 及不同参数传递方式):
package com.example.demo.controller;
import org.springframework.web.bind.annotation.*;
@restcontroller
public class democontroller {
// 简单 get,测试基本认证
@getmapping("/api/hello")
public string hello() {
return "hello, authenticated user!";
}
// post + 查询参数
@postmapping("/api/greet")
public string greet(@requestparam string name) {
return "hello, " + name;
}
// 路径参数
@getmapping("/users/{name}")
public string getuser(@pathvariable string name) {
return "user: " + name;
}
// post + json 请求体
@postmapping("/api/user/info")
public string receiveuser(@requestbody userinfo user) {
return "received user: " + user.getname();
}
// 内部静态 dto
static class userinfo {
private string name;
private string gender;
private string idnumber;
public string getname() { return name; }
public void setname(string name) { this.name = name; }
public string getgender() { return gender; }
public void setgender(string gender) { this.gender = gender; }
public string getidnumber() { return idnumber; }
public void setidnumber(string idnumber) { this.idnumber = idnumber; }
}
}将上述代码组织到 spring boot 工程中,启动后即可使用本文前述的 http client 请求进行测试。所有示例请求均与安全配置兼容,可以直接复制到 .http 文件中运行。
总结
http 请求的结构是认证与授权信息传递的物理基础。
理解起始行、各类请求头以及实体的作用,能帮助我们更精确地调试安全集成问题。从 http basic 到 oauth2 的 bearer token,本质都是在 authorization 头中放入不同格式的凭证。
而 csrf 保护、content-type 等机制则是安全与数据解析的边界守门员。后续进入 oauth2 的令牌传递、刷新、撤销流程时,这些 http 层面的细节将继续扮演关键角色。
到此这篇关于java框架快速入门: spring security+oauth2之http请求结构与认证基础的文章就介绍到这了,更多相关spring security oauth2内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论