首先,设置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请求发送给后端;否则,向客户端发送错误信息。
这部分的实现代码如下所示,先后对该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 }反向代理执行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 }
对于后续的开发过程中,对于Oathkeeper应该主要两个开发方向:
(1) 当收到HTTP请求后,增加除了认证用户、授权用户的其他操作,比如:从数据库中读取设备信息、验证设备信息、收集设备信息等内容,对于这类需求,直接在proxy.Director增加相应步骤即可。
(2) 增加认证、授权等其他组件,对于这类需求,可以按着如下步骤完成:
step1:在driver\registry_memory.go文件中对对应组件进行注册;
step2:完成相应认证、授权器,并加在authn,authz模块下
step3:进行配置,使用相应组件