【golang】GO之认证与授权

发布时间 2024-01-10 11:14:57作者: opensmarty

一般公司项目比较多,比较分散,但是对于都是公司的用户来说,用户数据一般是共享的,所以集成统一认证与授权的功能一般就必不可少,这样可以实现一个用户,分配一点权限,能访问公司很多项目.

一般的认证与授权方案有 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

 

未完待续....
【参考链接】