如何用Nginx搭建统一网关身份验证解决方案
最编程
2024-07-27 16:10:22
...
简介
当我们有多个服务需要共用一个鉴权接口时,使用Nginx也可以简单实现统一鉴权。
我们需要用到ngx_http_auth_request_module 模块,它可以基于子请求的结果实现客户端授权。
高版本(1.12+)已经默认安装该模块,或者使用--with-http_auth_request_module
开启此模块。
官方示例:
location /private/ {
auth_request /auth;
...
}
location = /auth {
proxy_pass ...
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
如果子请求返回 2xx 响应代码,则允许访问。如果返回 401 或 403,则使用相应的错误代码拒绝访问。子请求返回的任何其他响应代码都被视为错误。
对于 401 错误,客户端还从子请求响应中接收到WWW-Authenticate
请求头。
示例
我们来实现一个简单示例看他是如何工作的。
location /auth {
internal; # 只允许内部调用,外部调用报404
proxy_pass http://localhost:8081;
proxy_pass_request_body off; # 不向上游发送包体
proxy_set_header Content-Length ""; # 同上,看情况加或不加
proxy_set_header X-Original-URI $request_uri; # 传递真实请求路径
proxy_set_header X-Original-Remote-Addr $remote_addr; # 传递真实访问者地址
proxy_set_header X-Original-Host $host; # 传递真实请求地址
}
location /secret {
auth_request /auth;
auth_request_set $user $upstream_http_x_forwarded_user;
proxy_set_header X-User $user; # 可以传递Header
add_header Set-Cookie $user; # 可以传递Cookie
proxy_pass http://localhost:8080;
}
我这里开了两个服务,一个是localhost:8081/auth
提供鉴权服务,而访问localhost:8080/secret
服务时需要先鉴权。
@Slf4j
@RestController
public class DemoController {
@GetMapping("/auth")
public ResponseEntity auth(HttpServletResponse response) {
log.info("authing user");
response.addHeader("X-Forwarded-User", "username=root");
return ResponseEntity.ok("auth passed");
}
@GetMapping("/secret")
public ResponseEntity secret(HttpServletRequest request) {
String xUser = request.getHeader("X-User");
log.info("current user is {}", xUser);
return ResponseEntity.ok("see secret");
}
}
流程:
- 客户端请求
/secret
时,Nginx会将请求转发到/auth
- 我们在
/auth
接口中自定义鉴权流程,然后将结果写入响应体的X-Forwarded-User
头中 - 如果鉴权失败,Nginx会根据设置的状态码进行处理
- 如果鉴权成功,Nginx会将
X-Forwarded-User
赋值给$user
,再将$user
赋值给X-User
和Cookie - Nginx会携带
X-User
请求头和Cookie访问/secret
,我们在/secret
接口中可以拿到X-User
请求头和Cookie,可以用来做权限过滤 - 客户端收到
/secret
的响应时,也会带上Cookie
参考
Validating OAuth 2.0 Access Tokens with NGINX and NGINX Plus