大家好,希望您过得愉快!
我们将使用Auth0 Golang JWT中间件使用Gin Web框架来查看如何使用Auth0 Golang JWT中间件来验证JWT。杜松子酒是用GO(Golang)编写的网络框架。在深入研究中,我们将看到如何集成Auth0 Golang JWT中间件以验证JWTS
此博客是@ksivamuthu blog
的Alter版本
多亏了@ksivamuthu,我写了一个了不起的博客,我正在写这个博客,因为我无法在互联网中找到我的问题的精确解决方案,希望今天对您有所帮助
如果您遵循上述博客,您将了解有关auth0的基础知识,杜松子酒框架
基本上
用户流就像这样
(将CLI作为客户端)和黑匣子为服务器
我正在使用(可选)
首先遵循此blog
您必须先创建一个应用程序,并且我正在使用命令行应用程序,因此我创建了一个本机应用程序,我们必须创建用户和用户角色并分配给用户,为此,我创建了一个用户和角色在Auth0应用中具有权限“ CRUD:ADVINDSTINGS”并在应用程序中更新回调URL,我正在使用“ http://localhost:4242/auth”作为回电,请确保在使用auth0
之前设置所有内容您将auth0 api致电登录,如果成功登录,则给出一个令牌作为响应
基本上,我们必须将此JWT令牌用于我们的终点,但是在实际调用管理者之前,我们需要验证令牌和权限
示例备份的路由器GO代码
package api
import (
"context"
"errors"
"fmt"
"log"
"net/http"
"net/url"
"strings"
"github.com/gin-gonic/gin"
"github.com/lestrrat-go/jwx/jwk"
"github.com/lestrrat-go/jwx/jwt"
)
func StartServer() {
router := gin.Default()
ruoterGroup := router.Group("api/v1/product") //grouping the route end point for the handlers
ruoterGroup.GET("", AuthorizeHandler(permission), GetHandler)
ruoterGroup.POST("", AuthorizeHandler(permission), AddHandler)
ruoterGroup.PUT("", AuthorizeHandler(permission), UpdateHandler)
ruoterGroup.DELETE("", AuthorizeHandler(permission), DeleteHandler)
baseURL := :"localhost:8080"
router.Run(baseURL)
}
现在,您看到我们有一台在杜松子酒中创建的服务器和各自的处理程序,授权者应验证令牌并检查许可,在这里您可以拥有两个单独的功能来执行此操作,但我只创建了一个
现在在一个共同的地方我有这个函数定义
const (
authHeader = "Authorization"
permClaim = "permissions"
)
var permission = "crud:admin-settings" //your permissions here
var auth0Domain = "dev-******.us.auth0.com" //your auth0 domain here
var audience = "http://auth0-local.com"
func AuthorizeHandler(permission string) gin.HandlerFunc {
return func(c *gin.Context) {
token, err := extractToken(c, auth0Domain)
if err != nil {
fmt.Println(err.Error())
}
if token == nil {
fmt.Printf("failed to find token in context\n")
c.JSON(http.StatusUnauthorized, "user hasn't logged in yet")
c.Abort()
return
}
if !tokenHasPermission(token, permission) {
fmt.Printf("permission check failed\n")
c.AbortWithStatusJSON(500, "error occurred when authorizing user")
return
}
c.Next()
}
}
func tokenHasPermission(token jwt.Token, permission string) bool {
claims := token.PrivateClaims()
tkPermissions, ok := claims[permClaim]
if !ok {
return false
}
tkPermList, ok := tkPermissions.([]interface{})
if !ok {
return false
}
for _, perm := range tkPermList {
if perm == permission {
return true
}
}
return false
}
func extractToken(c *gin.Context, auth0Domain string) (jwt.Token, error) {
// fetchTenantKeys fetch and parse the tenant JSON Web Keys (JWK). The keys
// are used for JWT token validation during requests authorization.
tenantKeys, err := jwk.Fetch(context.Background(),
fmt.Sprintf("https://%s/.well-known/jwks.json", auth0Domain))
if err != nil {
log.Fatalf("failed to parse tenant json web keys: %s\n", err)
}
authorization := c.GetHeader(authHeader)
if authorization == "" {
return nil, errors.New("authorization header missing")
}
bearerAndToken := strings.Split(authorization, " ")
if len(bearerAndToken) < 2 {
return nil, errors.New("malformed authorization header: " + authorization)
}
token, err := jwt.Parse([]byte(bearerAndToken[1]), jwt.WithKeySet(tenantKeys),
jwt.WithValidate(true), jwt.WithAudience(audience))
if err != nil {
return nil, err
}
return token, nil
}
最后,在我的客户端中,我有此代码来获取令牌,基本上它将打开浏览器并转到Auth0网站并进行身份验证并将其归还给您可以用于进一步的REST API调用的令牌
package main
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"strings"
cv "github.com/nirasan/go-oauth-pkce-code-verifier"
"github.com/skratchdot/open-golang/open"
)
func main() {
//typically this values will come from config file
AuthorizeUser("ghxc6OoNP**************HnLdqJN", "dev-*******.auth0.com","http://localhost:4242/auth")
}
// AuthorizeUser implements the PKCE OAuth2 flow.
func AuthorizeUser(clientID string, authDomain string, redirectURL string) {
// initialize the code verifier
var CodeVerifier, _ = cv.CreateCodeVerifier()
// Create code_challenge with S256 method
codeChallenge := CodeVerifier.CodeChallengeS256()
// construct the authorization URL (with Auth0 as the authorization provider)
authorizationURL := fmt.Sprintf(
"https://%s/authorize?"+
"&scope=openid"+
"&response_type=code&client_id=%s"+
"&code_challenge=%s"+
"&code_challenge_method=S256&redirect_uri=%s",
authDomain, clientID, codeChallenge, redirectURL)
// start a web server to listen on a callback URL
server := &http.Server{Addr: redirectURL}
// define a handler that will get the authorization code, call the token endpoint, and close the HTTP server
http.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
// get the authorization code
code := r.URL.Query().Get("code")
if code == "" {
fmt.Println("snap: Url Param 'code' is missing")
io.WriteString(w, "Error: could not find 'code' URL parameter\n")
// close the HTTP server and return
cleanup(server)
return
}
// trade the authorization code and the code verifier for an access token
codeVerifier := CodeVerifier.String()
token, err := getAccessToken(clientID, codeVerifier, code, redirectURL)
if err != nil {
fmt.Println("snap: could not get access token")
io.WriteString(w, "Error: could not retrieve access token\n")
// close the HTTP server and return
cleanup(server)
return
}
//you can see the token
fmt.Println(token)
io.WriteString(w, `
<html>
<body>
<h1>Login successful!</h1>
<h2>You can close this window</h2>
</body>
</html>`)
fmt.Println("Successfully logged into snapmaster API.")
// close the HTTP server
cleanup(server)
})
// parse the redirect URL for the port number
u, err := url.Parse(redirectURL)
if err != nil {
fmt.Printf("snap: bad redirect URL: %s\n", err)
os.Exit(1)
}
// set up a listener on the redirect port
port := fmt.Sprintf(":%s", u.Port())
l, err := net.Listen("tcp", port)
if err != nil {
fmt.Printf("snap: can't listen to port %s: %s\n", port, err)
os.Exit(1)
}
// open a browser window to the authorizationURL
err = open.Start(authorizationURL)
if err != nil {
fmt.Printf("snap: can't open browser to URL %s: %s\n", authorizationURL, err)
os.Exit(1)
}
// start the blocking web server loop
// this will exit when the handler gets fired and calls server.Close()
server.Serve(l)
}
// getAccessToken trades the authorization code retrieved from the first OAuth2 leg for an access token
func getAccessToken(clientID string, codeVerifier string, authorizationCode string, callbackURL string) (string, error) {
// set the url and form-encoded data for the POST to the access token endpoint
fmt.Println("authorizationCode:", authorizationCode)
url := "https://dev-*******.us.auth0.com/oauth/token" //your auth0 domain
data := fmt.Sprintf(
"grant_type=authorization_code&client_id=%s"+
"&code_verifier=%s"+
"&code=%s"+
"&redirect_uri=%s",
clientID, codeVerifier, authorizationCode, callbackURL)
payload := strings.NewReader(data)
// create the request and execute it
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("content-type", "application/x-www-form-urlencoded")
res, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("snap: HTTP error: %s", err)
return "", err
}
// process the response
defer res.Body.Close()
var responseData map[string]interface{}
body, _ := ioutil.ReadAll(res.Body)
// unmarshal the json into a string map
err = json.Unmarshal(body, &responseData)
if err != nil {
fmt.Printf("snap: JSON error: %s", err)
return "", err
}
// retrieve the access token out of the map, and return to caller
//accessToken := responseData["access_token"].(string)
return "", nil
}
// cleanup closes the HTTP server
func cleanup(server *http.Server) {
// we run this as a goroutine so that this function falls through and
// the socket to the browser gets flushed/closed before the server goes away
go server.Close()
}
希望这将帮助一些人
感谢您的阅读,请评论如果您有任何疑问,
祝你有美好的一天