0 初始化
首先,设置Oathkeeper在cmd\server\server.go中,设置反向代理的两个重要执行函数:
handler := &httputil.ReverseProxy{
Director: proxy.Director,//通过proxy.Director完成对HTTP请求的认证等操作
Transport: proxy,//通过的proxy.RoundTrip完成与后端服务的通信
}
具体调用流程如下:
step1:当收到HTTP请求时,调用httputil.ReverseProxy.ServeHTTP对HTTP请求进行处理;
step2:调用proxy.Director,依据HTTP数据包的信息对HTTP请求进行认证、授权。
step3:调用proxy.RoundTrip,如果认证、授权阶段没有错误,则将HTTP请求发送给后端;否则,向客户端发送错误信息。
1 认证、鉴权及更改
这部分的实现代码如下所示,先后对该HTTP请求进行认证、授权:
/*
根据匹配到的规则,对HTTP请求进行认证、授权以及更改
*/
func (d *RequestHandler) HandleRequest(r *http.Request, rl *rule.Rule) (session *authn.AuthenticationSession, err error) {
var found bool
fields := map[string]interface{}{
"http_method": r.Method,
"http_url": r.URL.String(),
"http_host": r.Host,
"http_user_agent": r.UserAgent(),
"rule_id": rl.ID,
}
session = d.InitializeAuthnSession(r, rl)
//看一下这个规则是否有对应的认证器,不能没有
if len(rl.Authenticators) == 0 {
err = errors.New("No authentication handler was set in the rule")
d.r.Logger().WithError(err).
WithFields(fields).
WithField("granted", false).
WithField("reason_id", "authentication_handler_missing").
Warn("No authentication handler was set in the rule")
return nil, err
}
//Authenticator-->认证者
for _, a := range rl.Authenticators {
//看一下Oathkeeper是否支持该认证器
anh, err := d.r.PipelineAuthenticator(a.Handler)
if err != nil {
d.r.Logger().WithError(err).
WithFields(fields).
WithField("granted", false).
WithField("authentication_handler", a.Handler).
WithField("reason_id", "unknown_authentication_handler").
Warn("Unknown authentication handler requested")
return nil, err
}
//看一下这个认证器的配置是否符合要求
if err := anh.Validate(a.Config); err != nil {
d.r.Logger().WithError(err).
WithFields(fields).
WithField("granted", false).
WithField("authentication_handler", a.Handler).
WithField("reason_id", "invalid_authentication_handler").
Warn("Unable to validate use of authentication handler")
return nil, err
}
//执行认证过程,除非遇到ErrAuthenticatorNotResponsible错误,否则,都会返回该认证器的结果
err = anh.Authenticate(r, session, a.Config, rl)
if err != nil {
switch errors.Cause(err).Error() {
case authn.ErrAuthenticatorNotResponsible.Error():
// 如果有该异常,则尝试下一个认证器
break
case helper.ErrUnauthorized.ErrorField:
d.r.Logger().Info(err)
return nil, err
default:
d.r.Logger().WithError(err).
WithFields(fields).
WithField("granted", false).
WithField("authentication_handler", a.Handler).
WithField("reason_id", "authentication_handler_error").
Warn("The authentication handler encountered an error")
return nil, err
}
} else {
// The first authenticator that matches must return the session
found = true
fields["subject"] = session.Subject
break
}
}
/*
没有找到可用的认证器时,返回错误
*/
if !found {
err := errors.WithStack(helper.ErrUnauthorized)
d.r.Logger().WithError(err).
WithFields(fields).
WithField("granted", false).
WithField("reason_id", "authentication_handler_no_match").
Warn("No authentication handler was responsible for handling the authentication request")
return nil, err
}
//找到该服务器是否支持该认证器
azh, err := d.r.PipelineAuthorizer(rl.Authorizer.Handler)
if err != nil {
d.r.Logger().WithError(err).
WithFields(fields).
WithField("granted", false).
WithField("authorization_handler", rl.Authorizer.Handler).
WithField("reason_id", "unknown_authorization_handler").
Warn("Unknown authentication handler requested")
return nil, err
}
//查看配置是否符合该验证器的要求
if err := azh.Validate(rl.Authorizer.Config); err != nil {
d.r.Logger().WithError(err).
WithFields(fields).
WithField("granted", false).
WithField("authorization_handler", rl.Authorizer.Handler).
WithField("reason_id", "invalid_authorization_handler").
Warn("Unable to validate use of authorization handler")
return nil, err
}
//执行认证过程
if err := azh.Authorize(r, session, rl.Authorizer.Config, rl); err != nil {
d.r.Logger().
WithError(err).
WithFields(fields).
WithField("granted", false).
WithField("authorization_handler", rl.Authorizer.Handler).
WithField("reason_id", "authorization_handler_error").
Warn("The authorization handler encountered an error")
return nil, err
}
if len(rl.Mutators) == 0 {
err = errors.New("No mutation handler was set in the rule")
d.r.Logger().WithError(err).
WithFields(fields).
WithField("granted", false).
WithField("reason_id", "mutation_handler_missing").
Warn("No mutation handler was set in the rule")
return nil, err
}
//header变形器
for _, m := range rl.Mutators {
sh, err := d.r.PipelineMutator(m.Handler)
if err != nil {
d.r.Logger().WithError(err).
WithFields(fields).
WithField("granted", false).
WithField("access_url", r.URL.String()).
WithField("mutation_handler", m.Handler).
WithField("reason_id", "unknown_mutation_handler").
Warn("Unknown mutator requested")
return nil, err
}
if err := sh.Validate(m.Config); err != nil {
d.r.Logger().WithError(err).
WithFields(fields).
WithField("granted", false).
WithField("mutation_handler", m.Handler).
WithField("reason_id", "invalid_mutation_handler").
Warn("Invalid mutator requested")
return nil, err
}
if err := sh.Mutate(r, session, m.Config, rl); err != nil {
d.r.Logger().WithError(err).
WithFields(fields).
WithField("granted", false).
WithField("mutation_handler", m.Handler).
WithField("reason_id", "mutation_handler_error").
Warn("The mutation handler encountered an error")
return nil, err
}
}
return session, nil
}
2 将请求发到后端服务
反向代理执行RoundTrip函数将请求发往后端,并得到服务端响应:
func (d *Proxy) RoundTrip(r *http.Request) (*http.Response, error) {
rw := NewSimpleResponseWriter()
fields := map[string]interface{}{
"http_method": r.Method,
"http_url": r.URL.String(),
"http_host": r.Host,
"http_user_agent": r.UserAgent(),
}
if sess, ok := r.Context().Value(ContextKeySession).(*authn.AuthenticationSession); ok {
fields["subject"] = sess.Subject
}
rl, _ := r.Context().Value(ContextKeyMatchedRule).(*rule.Rule)
if err, ok := r.Context().Value(director).(error); ok && err != nil {
//如果认证时存在错误,则将错误信息返回给客户端
d.r.Logger().WithError(err).
WithFields(fields).
WithField("granted", false).
Warn("Access request denied")
d.r.ProxyRequestHandler().HandleError(rw, r, rl, err)
return &http.Response{
StatusCode: rw.code,
Body: ioutil.NopCloser(rw.buffer),
Header: rw.header,
}, nil
} else if err == nil {
//认证成功,把请求发给后端
res, err := http.DefaultTransport.RoundTrip(r)
if err != nil {
d.r.Logger().
WithError(errors.WithStack(err)).
WithField("granted", false).
WithFields(fields).
Warn("Access request denied because roundtrip failed")
// don't need to return because covered in next line
} else {
d.r.Logger().
WithField("granted", true).
WithFields(fields).
Warn("Access request granted")
}
return res, err
}
err := errors.New("Unable to type assert context")
d.r.Logger().
WithError(err).
WithField("granted", false).
WithFields(fields).
Warn("Unable to type assert context")
d.r.ProxyRequestHandler().HandleError(rw, r, rl, err)
return &http.Response{
StatusCode: rw.code,
Body: ioutil.NopCloser(rw.buffer),
Header: rw.header,
}, nil
}
3 反思
对于后续的开发过程中,对于Oathkeeper应该主要两个开发方向:
(1) 当收到HTTP请求后,增加除了认证用户、授权用户的其他操作,比如:从数据库中读取设备信息、验证设备信息、收集设备信息等内容,对于这类需求,直接在proxy.Director增加相应步骤即可。
(2) 增加认证、授权等其他组件,对于这类需求,可以按着如下步骤完成:
step1:在driver\registry_memory.go文件中对对应组件进行注册;
step2:完成相应认证、授权器,并加在authn,authz模块下
step3:进行配置,使用相应组件