一般公司项目比较多,比较分散,但是对于都是公司的用户来说,用户数据一般是共享的,所以集成统一认证与授权的功能一般就必不可少,这样可以实现一个用户,分配一点权限,能访问公司很多项目.
一般的认证与授权方案有 OAuth、分布式 Session、OpenID 和 JWT 等.目前常用的是OAuth2,其重点在于为Web应用程序、桌面应用程序、移动设备以及室内设备的授权流程提供简单的客户端开发方式。它为第三方应用提供对HTTP服务的有限访问,既可以是资源拥有者通过授权允许第三方应用获取HTTP服务,也可以是第三方以自己的名义获取访问权限。
OAuth2 中主要分为了4种角色(重点核心)
1.resource owner 资源所有者,是能够对受保护的资源授予访问权限的实体,可以是一个用户,这时会被称为end-user。
2.resource server 资源服务器,持有受保护的资源,允许持有访问令牌(access token)的请求访问受保护资源。
3.client 客户端,持有资源所有者的授权,代表资源所有者对受保护资源进行访问。
4.authorization server 授权服务器,对资源所有者的授权进行认证,成功后向客户端发送访问令牌。
(所有的操作都是围绕这四种角色来开展的.)
官方有一个流程图:
主要是六步操作:
1.client请求Resource owner 来 获取授权;
2.Resource owner 同意授权,返回授权许可(Authorization Grant);
3.client携带Authorization Grant要求授权Resource Server 进行认证,并发送一个token令牌;
4.Resource Server 对client进行身份验证,并认证Authorization Grant,如果有效,返回token令牌;
5.client携带Authorization Grant向Resource Server请求受保护资源的访问;
6.Resource Server验证token令牌,如果有效,接受访问请求,返回受保护资源。
授权类型
OAuth2默认定了四种授权类型
1.authorization code 授权码类型
2.implicit 简化类型(也称为隐式类型)
3.resource owner password credentials 密码类型
4. client credential 客户端类型
下面主要说一下客户端类型
具体代码如下:(着重看每个方法上面的注释,可以便于理解)
package main
import (
"encoding/json"
"fmt"
"github.com/google/uuid"
"gopkg.in/oauth2.v3/errors"
"gopkg.in/oauth2.v3/manage"
"gopkg.in/oauth2.v3/models"
"gopkg.in/oauth2.v3/server"
"gopkg.in/oauth2.v3/store"
"log"
"net/http"
)
func main(){
clientStore, srv := InitOauthManager()
/**
注册获取 clientId和clientSecret( 1.client请求Resource owner 来 获取授权; )
*/
http.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) {
clientId := uuid.New().String()[:8]
clientSecret := uuid.New().String()[:8]
err := clientStore.Set(clientId, &models.Client{
ID: clientId,
Secret: clientSecret,
})
if err != nil {
fmt.Println(err.Error())
}
w.Header().Set("Content-Type", "application/json")
/**
//(2.Resource owner 同意授权,返回授权许可(Authorization Grant))
*/
json.NewEncoder(w).Encode(map[string]string{"CLIENT_ID": clientId, "CLIENT_SECRET": clientSecret}) //实际项目中一般存到数据库或者redis
})
/**
获取access_token( 3.client携带Authorization Grant要求授权Resource Server 进行认证,并发送一个token令牌;
4.Resource Server 对client进行身份验证,并认证Authorization Grant,如果有效,返回token令牌)
*/
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
srv.HandleTokenRequest(w, r)
})
/**
带着access_token 访问路径(5.client携带Authorization Grant向Resource Server请求受保护资源的访问;)
*/
http.HandleFunc("/test01", validateToken(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("HelloWord"))
}, srv))
log.Fatal(http.ListenAndServe(":8888", nil))
}
/**
token校验(6.Resource Server验证token令牌,如果有效,接受访问请求,返回受保护资源。)
*/
func validateToken(f http.HandlerFunc, srv *server.Server) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := srv.ValidationBearerToken(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
f.ServeHTTP(w, r)
})
}
//初始化OauthManager
func InitOauthManager() (*store.ClientStore, *server.Server) {
manager := manage.NewDefaultManager()
manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg)
manager.MustTokenStorage(store.NewMemoryTokenStore())
clientStore := store.NewClientStore()
manager.MapClientStorage(clientStore)
srv := server.NewDefaultServer(manager)
srv.SetAllowGetAccessRequest(true)
srv.SetClientInfoHandler(server.ClientFormHandler)
manager.SetRefreshTokenCfg(manage.DefaultRefreshTokenCfg)
srv.SetInternalErrorHandler(func(err error) (re *errors.Response) {
log.Println("Internal Error:", err.Error())
return
})
srv.SetResponseErrorHandler(func(re *errors.Response) {
log.Println("Response Error:", re.Error.Error())
})
return clientStore, srv
}
1.首先我们启动项目.访问: http://localhost:8888/route 获取 clientId 和 clientSecret
2.然后访问: http://localhost:8888/token?grant_type=client_credentials&client_id=012eaf30&client_secret=298262d9 获取 到 access_token
3.在访问 :http://localhost:8888/test01?access_token=P8AR0LRKOLOYEMV9ACUXWG 可以看到返回的值
4.我们不加access_token或者修改下这个access_token 看是否还能获取到响应内容呢?
1.修改access_token 发现:
2.不加access_token
可以看到 没有正确的token令牌 是不让我们访问的,至此,我们整个模拟流程走通了.
Oauth支持的5类 grant_type 及说明
authorization_code — 授权码模式(即先登录获取code,再获取token)
password — 密码模式(将用户名,密码传过去,直接获取token)
client_credentials — 客户端模式(无用户,用户向客户端注册,然后客户端以自己的名义向’服务端’获取资源)
implicit — 简化模式(在redirect_uri 的Hash传递token; Auth客户端运行在浏览器中,如JS,Flash)
refresh_token — 刷新access_token
未完待续....