go build .
yarn build
编写Dockerfile文件:
FROM alpine
RUN echo -e https://mirrors.ustc.edu.cn/alpine/v3.15/main > /etc/apk/repositories \
&& cat /etc/apk/repositories \
# 设置时区为上海
&& apk add tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& apk del tzdata \
# 解决apline 运行编译后的执行文件 not found错误
# 由于alpine镜像使用的是musl libc而不是gnu libc,/lib64/ 是不存在的。但他们是兼容的,可以创建个软连接
&& mkdir /lib64 \
&& ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
WORKDIR /
ADD web web
ADD casdoor casdoor
EXPOSE 8000
ENTRYPOINT ["./casdoor"]
编写镜像构建脚本:
#!/bin/bash
# 命名镜像
repository_image="registry.cn-chengdu.aliyuncs.com/infish/pack-casdoor-auth:v1.0.0"
# 删除本地已存在的镜像
docker rmi $repository_image
# 创建本地镜像
docker build -t $repository_image .
# push到镜像仓库,需要登陆对应docker仓库账号
docker push $repository_image
编写docker-compose.yaml文件:
version: '3.8'
# 网络
networks:
default:
name: default-network
external: true
services:
auth-casdoor:
image: "registry.cn-chengdu.aliyuncs.com/infish/pack-casdoor-auth:v1.0.0"
restart: always
ports:
- 36002:8000
volumes:
- ./conf:/conf
depends_on:
- auth-mysql
auth-mysql:
restart: always
# 5.7不支持中文
image: mysql:8
volumes:
- /data/auth/mysql:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: auth2023
MYSQL_DATABASE: casdoor
ports:
- 33306:3306
conf/app.conf文件:
ppname = casdoor
httpport = 8000
runmode = dev
copyrequestbody = true
driverName = mysql
dataSourceName = root:auth2023@tcp(auth-mysql:3306)/
dbName = casdoor
# tableNamePrefix =
# showSql = false
# redisEndpoint =
# defaultStorageProvider =
# isCloudIntranet = false
# authState = "casdoor"
# socks5Proxy = "127.0.0.1:10808"
# verificationCodeTimeout = 10
# initScore = 2000
# logPostOnly = true
# origin =
# staticBaseUrl = "https://auth3dqueen.oss-cn-beijing.aliyuncs.com/static"
# isDemoMode = false
# batchSize = 100
# ldapServerPort = 389
# languages = en,zh,es,fr,de,id,ja,ko,ru,vi
# quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}
启动服务:docker compose up -d
配置nginx:
server {
listen 80;
listen [::]:80;
server_name auth.3dqueen.cloud;
rewrite ^(.*) https://$server_name$1 permanent;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name auth.3dqueen.cloud;
#root /var/www/auth;
client_max_body_size 5M;
ssl_certificate "cert/9743199_auth.3dqueen.cloud.pem";
ssl_certificate_key "cert/9743199_auth.3dqueen.cloud.key";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
proxy_pass http://127.0.0.1:36002;
}
}
访问nginx配置的域名,登录后配置组织和应用:账号:admin 密码:123
下载证书、配置到后端:
app.yaml:
...
Auth:
endpoint: https://auth.3dqueen.cloud
clientId: 9f22d6616ae14fe59dc4
clientSecret: b0fae13ca7e7eb1e4e6dc8fa50902eb7b6035049
certificate: token_jwt_key.pem
organizationName: org_3dqueen_cloud
applicationName: app_boxcost
Dockerfile文件中添加:
...
# 下载的证书
ADD token_jwt_key.pem token_jwt_key.pem
...
下载使用casdoor-go-sdk
配置回调:api/calback.go
// https://auth.3dqueen.cloud/login/oauth/authorize?client_id=9f22d6616ae14fe59dc4&redirect_uri=https://www.3dqueen.cloud/box/v1/boxcost/callback&response_type=code&scope=openid&state=STATE
// 需要在第三方提供商配置回调 https://auth.3dqueen.cloud/callback
func callback(c *gin.Context, apictx *ApiSession) (interface{}, error) {
authConf := apictx.Svc.Conf.Auth
pemByte, err := os.ReadFile(authConf.Certificate)
if err != nil {
return nil, err
}
casdoorsdk.InitConfig(authConf.Endpoint, authConf.ClientId, authConf.ClientSecret, string(pemByte), authConf.OrganizationName, authConf.ApplicationName)
token, err := casdoorsdk.GetOAuthToken(c.Query("code"), c.Query("state"))
if err != nil {
fmt.Println(err)
}
fmt.Println(token.AccessToken)
claims, err := casdoorsdk.ParseJwtToken(token.AccessToken)
if err != nil {
fmt.Println(err)
}
fmt.Println(claims)
c.Redirect(http.StatusFound, "https://auth.3dqueen.cloud")
// claims.AccessToken = token.AccessToken
return nil, nil
}
password方式获取token:api/auth.go
func AuthToken(c *gin.Context, apictx *ApiSession) (interface{}, error) {
authConf := apictx.Svc.Conf.Auth
// pemByte, err := os.ReadFile(authConf.Certificate)
// if err != nil {
// return nil, err
// }
// casdoorsdk.InitConfig(authConf.Endpoint, authConf.ClientId, authConf.ClientSecret, string(pemByte), authConf.OrganizationName, authConf.ApplicationName)
data := &AuthTOkenReq{
GrantType: "password",
ClientId: authConf.ClientId,
ClientSecret: authConf.ClientSecret,
UserName: "sunsheng",
Password: "ssxhyw2515",
}
dataByte, err := json.Marshal(data)
if err != nil {
return nil, err
}
fmt.Println(dataByte)
// 发送POST请求并将数据作为JSON提交
req, err := http.NewRequest("POST", fmt.Sprintf("%s/%s", authConf.Endpoint, "api/login/oauth/access_token"), bytes.NewBuffer(dataByte))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var buf bytes.Buffer
_, err = buf.ReadFrom(resp.Body)
if err != nil {
panic(err)
}
buf.WriteTo(c.Writer)
// responseStr := buf.String()
// 打印响应内容
// fmt.Println(responseStr)
return nil, nil
}
jwt中间件:middleware/jwt.go
package middleware
import (
"fmt"
"net/http"
"strings"
"time"
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
"github.com/gin-gonic/gin"
)
// JWTAuthMiddleware 基于JWT的认证中间件--验证用户是否登录
func JWTAuthMiddleware() func(c *gin.Context) {
return func(c *gin.Context) {
authHeader := c.Request.Header.Get("authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 2003,
"msg": "请求头中auth为空",
})
c.Abort()
return
}
// 按空格分割
parts := strings.Split(authHeader, ".")
if len(parts) != 3 {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 2004,
"msg": "请求头中auth格式有误",
})
c.Abort()
return
}
mc, err := casdoorsdk.ParseJwtToken(authHeader)
fmt.Println(mc.VerifyExpiresAt(time.Now(), false))
if err != nil {
fmt.Println(err)
c.JSON(http.StatusUnauthorized, gin.H{
"code": 2005,
"msg": "无效的Token",
})
c.Abort()
return
}
// m := mc.(jwt.MapClaims)
// 将当前请求的username信息保存到请求的上下文c上
c.Set("uid", mc.User.Id)
c.Set("username", mc.User.Name)
c.Next() // 后续的处理函数可以用过c.Get("username")来获取当前请求的用户信息
}
}
权限控制中间件:middleware/pc.go
package middleware
import (
"boxcost/conf"
"fmt"
"net/http"
"strings"
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
"github.com/gin-gonic/gin"
)
func PermissionControlMiddleware() func(c *gin.Context) {
return func(c *gin.Context) {
// 验证该用户是否可以访问该资源
permission := strings.Replace(c.Request.URL.String(), "/", "_", -1)
id := fmt.Sprintf("%s/%s%s", conf.AppConfig.Auth.OrganizationName, strings.ToLower(c.Request.Method), permission)
fmt.Println(id)
ok, err := casdoorsdk.Enforce(&casdoorsdk.PermissionRule{
Ptype: "p",
V0: fmt.Sprintf("%s/%s", conf.AppConfig.Auth.OrganizationName, c.GetString("username")), // 请求实体
V1: c.Request.URL.String(), // 请求资源主体 // /book/*
V2: strings.ToLower(c.Request.Method), // 请求方法
Id: id,
})
// roles
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{
"code": -1,
"msg": err.Error(),
})
c.Abort()
return
}
if !ok {
c.JSON(http.StatusForbidden, gin.H{
"code": -1,
"msg": "没有权限访问",
})
c.Abort()
return
}
c.Next()
}
}
使用中间件:api/api.go
...
// GETJWT http Get 请求
func (g GinRouter) GETJWT(path string, httpHandler JWTHander) {
g.group.GET(path, middleware.JWTAuthMiddleware(), middleware.PermissionControlMiddleware(), ResultJWTWrapper(httpHandler, g.svc))
}
// POSTJWT http POST 请求
func (g GinRouter) POSTJWT(path string, httpHandler JWTHander) {
g.group.POST(path, middleware.JWTAuthMiddleware(), middleware.PermissionControlMiddleware(), ResultJWTWrapper(httpHandler, g.svc))
}
...
测试:api/user.go
package api
import (
"afina/conf"
"fmt"
"time"
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
"github.com/gin-gonic/gin"
)
func User(r *GinRouter) {
r.POSTJWT("/user/create", CreateUser)
}
func CreateUser(c *gin.Context, apictx *ApiSession) (interface{}, error) {
var user casdoorsdk.User
err := c.ShouldBindJSON(&user)
if err != nil {
fmt.Println(err)
return nil, err
}
user.SignupApplication = conf.AppConfig.Auth.ApplicationName
user.CreatedTime = time.Now().Format("2006-01-02T15:04:05-07:00")
user.Type = "normal-user"
return casdoorsdk.AddUser(&user)
}