mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
feat: added casdoor sso support (#204)
This commit is contained in:
parent
34289150fa
commit
7fe3517afe
9 changed files with 180 additions and 26 deletions
|
@ -8,6 +8,13 @@ StartCmd = login
|
||||||
Database = database
|
Database = database
|
||||||
CADir =
|
CADir =
|
||||||
Demo =
|
Demo =
|
||||||
|
CasdoorEndpoint =
|
||||||
|
CasdoorClientId =
|
||||||
|
CasdoorClientSecret =
|
||||||
|
CasdoorCertificate =
|
||||||
|
CasdoorOrganization =
|
||||||
|
CasdoorApplication =
|
||||||
|
CasdoorRedirectUri =
|
||||||
|
|
||||||
[nginx]
|
[nginx]
|
||||||
AccessLogPath = /var/log/nginx/access.log
|
AccessLogPath = /var/log/nginx/access.log
|
||||||
|
|
|
@ -13,6 +13,15 @@ const auth = {
|
||||||
login(r.token)
|
login(r.token)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
async casdoorLogin(code: string, state: string) {
|
||||||
|
await http.post("/casdoor_callback", {
|
||||||
|
code: code,
|
||||||
|
state: state,
|
||||||
|
})
|
||||||
|
.then((r) => {
|
||||||
|
login(r.token)
|
||||||
|
})
|
||||||
|
},
|
||||||
logout() {
|
logout() {
|
||||||
return http.delete('/logout').then(async () => {
|
return http.delete('/logout').then(async () => {
|
||||||
logout()
|
logout()
|
||||||
|
|
|
@ -8,6 +8,8 @@ import {Form, message} from 'ant-design-vue'
|
||||||
import auth from '@/api/auth'
|
import auth from '@/api/auth'
|
||||||
import install from '@/api/install'
|
import install from '@/api/install'
|
||||||
import SetLanguage from '@/components/SetLanguage/SetLanguage.vue'
|
import SetLanguage from '@/components/SetLanguage/SetLanguage.vue'
|
||||||
|
import http from '@/lib/http'
|
||||||
|
import {onMounted} from 'vue'
|
||||||
|
|
||||||
const thisYear = new Date().getFullYear()
|
const thisYear = new Date().getFullYear()
|
||||||
|
|
||||||
|
@ -70,6 +72,37 @@ watch(() => gettext.current, () => {
|
||||||
clearValidate()
|
clearValidate()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const has_casdoor = ref(false)
|
||||||
|
const casdoor_uri = ref('')
|
||||||
|
|
||||||
|
http.get("/casdoor_uri")
|
||||||
|
.then((response) => {
|
||||||
|
if (response?.uri) {
|
||||||
|
has_casdoor.value = true
|
||||||
|
casdoor_uri.value = response.uri
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
message.error($gettext(e.message ?? 'Server error'))
|
||||||
|
});
|
||||||
|
|
||||||
|
const loginWithCasdoor = () => {
|
||||||
|
window.location.href = casdoor_uri.value
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.query?.code != undefined && route.query?.state != undefined) {
|
||||||
|
loading.value = true
|
||||||
|
auth.casdoorLogin(route.query.code.toString(), route.query.state.toString()).then(async () => {
|
||||||
|
message.success($gettext('Login successful'), 1)
|
||||||
|
const next = (route.query?.next || '').toString() || '/'
|
||||||
|
await router.push(next)
|
||||||
|
}).catch(e => {
|
||||||
|
message.error($gettext(e.message ?? 'Server error'))
|
||||||
|
})
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -105,6 +138,9 @@ watch(() => gettext.current, () => {
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
<a-button @click="loginWithCasdoor" :block="true" html-type="submit" :loading="loading" v-if="has_casdoor">
|
||||||
|
{{ $gettext('SSO Login') }}
|
||||||
|
</a-button>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<p>Copyright © 2020 - {{ thisYear }} Nginx UI</p>
|
<p>Copyright © 2020 - {{ thisYear }} Nginx UI</p>
|
||||||
Language
|
Language
|
||||||
|
|
3
go.mod
3
go.mod
|
@ -7,6 +7,7 @@ toolchain go1.21.0
|
||||||
require (
|
require (
|
||||||
github.com/0xJacky/pofile v0.2.1
|
github.com/0xJacky/pofile v0.2.1
|
||||||
github.com/BurntSushi/toml v1.3.2
|
github.com/BurntSushi/toml v1.3.2
|
||||||
|
github.com/casdoor/casdoor-go-sdk v0.32.1
|
||||||
github.com/creack/pty v1.1.20
|
github.com/creack/pty v1.1.20
|
||||||
github.com/dustin/go-humanize v1.0.1
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/fatih/color v1.15.0
|
github.com/fatih/color v1.15.0
|
||||||
|
@ -236,7 +237,7 @@ require (
|
||||||
golang.org/x/arch v0.5.0 // indirect
|
golang.org/x/arch v0.5.0 // indirect
|
||||||
golang.org/x/mod v0.12.0 // indirect
|
golang.org/x/mod v0.12.0 // indirect
|
||||||
golang.org/x/net v0.17.0 // indirect
|
golang.org/x/net v0.17.0 // indirect
|
||||||
golang.org/x/oauth2 v0.12.0 // indirect
|
golang.org/x/oauth2 v0.13.0 // indirect
|
||||||
golang.org/x/sys v0.13.0 // indirect
|
golang.org/x/sys v0.13.0 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -149,6 +149,8 @@ github.com/bytedance/sonic v1.10.0 h1:qtNZduETEIWJVIyDl01BeNxur2rW9OwTQ/yBqFRkKE
|
||||||
github.com/bytedance/sonic v1.10.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
github.com/bytedance/sonic v1.10.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||||
github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=
|
github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=
|
||||||
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
||||||
|
github.com/casdoor/casdoor-go-sdk v0.32.1 h1:SMMbicerANhceSiYdzFN2woZyf63aPemvZVT7fsJu+A=
|
||||||
|
github.com/casdoor/casdoor-go-sdk v0.32.1/go.mod h1:hVSgmSdwTCsBEJNt9r2K5aLVsoeMc37/N4Zzescy5SA=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
@ -1013,8 +1015,8 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
|
golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
|
||||||
golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
|
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/0xJacky/Nginx-UI/server/model"
|
"github.com/0xJacky/Nginx-UI/server/model"
|
||||||
|
"github.com/0xJacky/Nginx-UI/server/settings"
|
||||||
|
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -55,3 +58,62 @@ func Logout(c *gin.Context) {
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusNoContent, nil)
|
c.JSON(http.StatusNoContent, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CasdoorLoginUser struct {
|
||||||
|
Code string `json:"code" binding:"required,max=255"`
|
||||||
|
State string `json:"state" binding:"required,max=255"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CasdoorCallback(c *gin.Context) {
|
||||||
|
var loginUser CasdoorLoginUser
|
||||||
|
fmt.Println("CasdoorCallback called")
|
||||||
|
ok := BindAndValid(c, &loginUser)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
endpoint := settings.ServerSettings.CasdoorEndpoint
|
||||||
|
clientId := settings.ServerSettings.CasdoorClientId
|
||||||
|
clientSecret := settings.ServerSettings.CasdoorClientSecret
|
||||||
|
certificate := settings.ServerSettings.CasdoorCertificate
|
||||||
|
organization := settings.ServerSettings.CasdoorOrganization
|
||||||
|
application := settings.ServerSettings.CasdoorApplication
|
||||||
|
if endpoint == "" || clientId == "" || clientSecret == "" || certificate == "" || organization == "" || application == "" {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
|
"message": "Casdoor is not configured",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
casdoorsdk.InitConfig(endpoint, clientId, clientSecret, certificate, organization, application)
|
||||||
|
token, err := casdoorsdk.GetOAuthToken(loginUser.Code, loginUser.State)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
|
"message": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims, err := casdoorsdk.ParseJwtToken(token.AccessToken)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
|
"message": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u, err := model.GetUser(claims.Name)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
|
"message": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
userToken, err := model.GenerateJWT(u.Name)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
|
"message": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"message": "ok",
|
||||||
|
"token": userToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/0xJacky/Nginx-UI/server/settings"
|
"github.com/0xJacky/Nginx-UI/server/settings"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetSettings(c *gin.Context) {
|
func GetSettings(c *gin.Context) {
|
||||||
|
@ -39,3 +41,21 @@ func SaveSettings(c *gin.Context) {
|
||||||
|
|
||||||
GetSettings(c)
|
GetSettings(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetCasdoorUri(c *gin.Context) {
|
||||||
|
endpoint := settings.ServerSettings.CasdoorEndpoint
|
||||||
|
clientId := settings.ServerSettings.CasdoorClientId
|
||||||
|
redirectUri := settings.ServerSettings.CasdoorRedirectUri
|
||||||
|
state := settings.ServerSettings.CasdoorApplication
|
||||||
|
fmt.Println(redirectUri)
|
||||||
|
if endpoint == "" || clientId == "" || redirectUri == "" || state == "" {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"uri": "",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
encodedRedirectUri := url.QueryEscape(redirectUri)
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"uri": fmt.Sprintf("%s/login/oauth/authorize?client_id=%s&response_type=code&redirect_uri=%s&state=%s&scope=read", endpoint, clientId, encodedRedirectUri, state),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,9 @@ func InitRouter() *gin.Engine {
|
||||||
root.POST("/login", api.Login)
|
root.POST("/login", api.Login)
|
||||||
root.DELETE("/logout", api.Logout)
|
root.DELETE("/logout", api.Logout)
|
||||||
|
|
||||||
|
root.GET("/casdoor_uri", api.GetCasdoorUri)
|
||||||
|
root.POST("/casdoor_callback", api.CasdoorCallback)
|
||||||
|
|
||||||
// translation
|
// translation
|
||||||
root.GET("translation/:code", api.GetTranslation)
|
root.GET("translation/:code", api.GetTranslation)
|
||||||
|
|
||||||
|
|
|
@ -16,19 +16,26 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
HttpHost string `json:"http_host"`
|
HttpHost string `json:"http_host"`
|
||||||
HttpPort string `json:"http_port"`
|
HttpPort string `json:"http_port"`
|
||||||
RunMode string `json:"run_mode"`
|
RunMode string `json:"run_mode"`
|
||||||
JwtSecret string `json:"jwt_secret"`
|
JwtSecret string `json:"jwt_secret"`
|
||||||
NodeSecret string `json:"node_secret"`
|
NodeSecret string `json:"node_secret"`
|
||||||
HTTPChallengePort string `json:"http_challenge_port"`
|
HTTPChallengePort string `json:"http_challenge_port"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Database string `json:"database"`
|
Database string `json:"database"`
|
||||||
StartCmd string `json:"start_cmd"`
|
StartCmd string `json:"start_cmd"`
|
||||||
CADir string `json:"ca_dir"`
|
CADir string `json:"ca_dir"`
|
||||||
Demo bool `json:"demo"`
|
Demo bool `json:"demo"`
|
||||||
PageSize int `json:"page_size"`
|
PageSize int `json:"page_size"`
|
||||||
GithubProxy string `json:"github_proxy"`
|
GithubProxy string `json:"github_proxy"`
|
||||||
|
CasdoorEndpoint string `json:"casdoor_endpoint"`
|
||||||
|
CasdoorClientId string `json:"casdoor_client_id"`
|
||||||
|
CasdoorClientSecret string `json:"casdoor_client_secret"`
|
||||||
|
CasdoorCertificate string `json:"casdoor_certificate"`
|
||||||
|
CasdoorOrganization string `json:"casdoor_organization"`
|
||||||
|
CasdoorApplication string `json:"casdoor_application"`
|
||||||
|
CasdoorRedirectUri string `json:"casdoor_redirect_uri"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Nginx struct {
|
type Nginx struct {
|
||||||
|
@ -49,16 +56,23 @@ type OpenAI struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var ServerSettings = Server{
|
var ServerSettings = Server{
|
||||||
HttpHost: "0.0.0.0",
|
HttpHost: "0.0.0.0",
|
||||||
HttpPort: "9000",
|
HttpPort: "9000",
|
||||||
RunMode: "debug",
|
RunMode: "debug",
|
||||||
HTTPChallengePort: "9180",
|
HTTPChallengePort: "9180",
|
||||||
Database: "database",
|
Database: "database",
|
||||||
StartCmd: "login",
|
StartCmd: "login",
|
||||||
Demo: false,
|
Demo: false,
|
||||||
PageSize: 10,
|
PageSize: 10,
|
||||||
CADir: "",
|
CADir: "",
|
||||||
GithubProxy: "",
|
GithubProxy: "",
|
||||||
|
CasdoorEndpoint: "",
|
||||||
|
CasdoorClientId: "",
|
||||||
|
CasdoorClientSecret: "",
|
||||||
|
CasdoorCertificate: "",
|
||||||
|
CasdoorOrganization: "",
|
||||||
|
CasdoorApplication: "",
|
||||||
|
CasdoorRedirectUri: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
var NginxSettings = Nginx{
|
var NginxSettings = Nginx{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue