commit
0b047a9384
@ -0,0 +1,3 @@ |
|||||||
|
/.idea |
||||||
|
/*.iml |
||||||
|
.DS_Store |
@ -0,0 +1,24 @@ |
|||||||
|
module nucleus |
||||||
|
|
||||||
|
go 1.20 |
||||||
|
|
||||||
|
require ( |
||||||
|
github.com/go-oauth2/oauth2/v4 v4.5.2 |
||||||
|
github.com/go-session/session v3.1.2+incompatible |
||||||
|
gorm.io/gorm v1.25.2 |
||||||
|
) |
||||||
|
|
||||||
|
require ( |
||||||
|
github.com/golang-jwt/jwt v3.2.1+incompatible // indirect |
||||||
|
github.com/google/uuid v1.1.1 // indirect |
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect |
||||||
|
github.com/jinzhu/now v1.1.5 // indirect |
||||||
|
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274 // indirect |
||||||
|
github.com/tidwall/buntdb v1.1.2 // indirect |
||||||
|
github.com/tidwall/gjson v1.12.1 // indirect |
||||||
|
github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb // indirect |
||||||
|
github.com/tidwall/match v1.1.1 // indirect |
||||||
|
github.com/tidwall/pretty v1.2.0 // indirect |
||||||
|
github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e // indirect |
||||||
|
github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 // indirect |
||||||
|
) |
@ -0,0 +1,173 @@ |
|||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= |
||||||
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= |
||||||
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= |
||||||
|
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= |
||||||
|
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= |
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= |
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||||
|
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= |
||||||
|
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= |
||||||
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= |
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= |
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= |
||||||
|
github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8= |
||||||
|
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= |
||||||
|
github.com/go-oauth2/oauth2/v4 v4.5.2 h1:CuZhD3lhGuI6aNLyUbRHXsgG2RwGRBOuCBfd4WQKqBQ= |
||||||
|
github.com/go-oauth2/oauth2/v4 v4.5.2/go.mod h1:wk/2uLImWIa9VVQDgxz99H2GDbhmfi/9/Xr+GvkSUSQ= |
||||||
|
github.com/go-session/session v3.1.2+incompatible h1:yStchEObKg4nk2F7JGE7KoFIrA/1Y078peagMWcrncg= |
||||||
|
github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0= |
||||||
|
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= |
||||||
|
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= |
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= |
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= |
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= |
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= |
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= |
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= |
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= |
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= |
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= |
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |
||||||
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= |
||||||
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= |
||||||
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= |
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= |
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= |
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= |
||||||
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= |
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= |
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= |
||||||
|
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= |
||||||
|
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= |
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= |
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= |
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= |
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= |
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= |
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= |
||||||
|
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= |
||||||
|
github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U= |
||||||
|
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= |
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= |
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= |
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= |
||||||
|
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= |
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= |
||||||
|
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= |
||||||
|
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= |
||||||
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= |
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= |
||||||
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= |
||||||
|
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= |
||||||
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= |
||||||
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= |
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
||||||
|
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= |
||||||
|
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= |
||||||
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= |
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= |
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= |
||||||
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= |
||||||
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= |
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= |
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= |
||||||
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= |
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
||||||
|
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274 h1:G6Z6HvJuPjG6XfNGi/feOATzeJrfgTNJY+rGrHbA04E= |
||||||
|
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8= |
||||||
|
github.com/tidwall/buntdb v1.1.2 h1:noCrqQXL9EKMtcdwJcmuVKSEjqu1ua99RHHgbLTEHRo= |
||||||
|
github.com/tidwall/buntdb v1.1.2/go.mod h1:xAzi36Hir4FarpSHyfuZ6JzPJdjRZ8QlLZSntE2mqlI= |
||||||
|
github.com/tidwall/gjson v1.3.4/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= |
||||||
|
github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo= |
||||||
|
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= |
||||||
|
github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb h1:5NSYaAdrnblKByzd7XByQEJVT8+9v0W/tIY0Oo4OwrE= |
||||||
|
github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb/go.mod h1:lKYYLFIr9OIgdgrtgkZ9zgRxRdvPYsExnYBsEAd8W5M= |
||||||
|
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= |
||||||
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= |
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= |
||||||
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= |
||||||
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= |
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= |
||||||
|
github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e h1:+NL1GDIUOKxVfbp2KoJQD9cTQ6dyP2co9q4yzmT9FZo= |
||||||
|
github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e/go.mod h1:/h+UnNGt0IhNNJLkGikcdcJqm66zGD/uJGMRxK/9+Ao= |
||||||
|
github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 h1:Otn9S136ELckZ3KKDyCkxapfufrqDqwmGjcHfAyXRrE= |
||||||
|
github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563/go.mod h1:mLqSmt7Dv/CNneF2wfcChfN1rvapyQr01LGKnKex0DQ= |
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= |
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= |
||||||
|
github.com/valyala/fasthttp v1.34.0 h1:d3AAQJ2DRcxJYHm7OXNXtXt2as1vMDfxeIcFvhmGGm4= |
||||||
|
github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0= |
||||||
|
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= |
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= |
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= |
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= |
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= |
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= |
||||||
|
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= |
||||||
|
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= |
||||||
|
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= |
||||||
|
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= |
||||||
|
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= |
||||||
|
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= |
||||||
|
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= |
||||||
|
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= |
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |
||||||
|
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= |
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= |
||||||
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= |
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= |
||||||
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= |
||||||
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= |
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= |
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||||
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||||
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||||
|
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= |
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= |
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= |
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= |
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= |
||||||
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= |
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= |
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= |
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= |
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= |
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= |
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= |
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= |
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= |
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= |
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
||||||
|
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho= |
||||||
|
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= |
@ -0,0 +1,21 @@ |
|||||||
|
package internal |
||||||
|
|
||||||
|
import ( |
||||||
|
"log" |
||||||
|
"net/http" |
||||||
|
"os" |
||||||
|
) |
||||||
|
|
||||||
|
type Controller struct{} |
||||||
|
|
||||||
|
func (c *Controller) Render(w http.ResponseWriter, r *http.Request, t string) { |
||||||
|
file, err := os.Open(t) |
||||||
|
if err != nil { |
||||||
|
log.Println(err) |
||||||
|
http.Error(w, err.Error(), 500) |
||||||
|
return |
||||||
|
} |
||||||
|
defer file.Close() |
||||||
|
fi, _ := file.Stat() |
||||||
|
http.ServeContent(w, r, file.Name(), fi.ModTime(), file) |
||||||
|
} |
@ -0,0 +1,223 @@ |
|||||||
|
package controllers |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
"github.com/go-oauth2/oauth2/v4" |
||||||
|
"github.com/go-oauth2/oauth2/v4/manage" |
||||||
|
"github.com/go-oauth2/oauth2/v4/models" |
||||||
|
"github.com/go-oauth2/oauth2/v4/server" |
||||||
|
"github.com/go-oauth2/oauth2/v4/store" |
||||||
|
"github.com/go-session/session" |
||||||
|
"log" |
||||||
|
"net/http" |
||||||
|
"nucleus/internal" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
type UserInfo struct { |
||||||
|
Username string `json:"username"` |
||||||
|
Gender string `json:"gender"` |
||||||
|
} |
||||||
|
|
||||||
|
var manager *manage.Manager |
||||||
|
var srv *server.Server |
||||||
|
var userInfoMap = make(map[string]UserInfo) |
||||||
|
|
||||||
|
type Oauth2Controller struct { |
||||||
|
internal.Controller |
||||||
|
} |
||||||
|
|
||||||
|
func (c *Oauth2Controller) Init() { |
||||||
|
clientStore := store.NewClientStore() |
||||||
|
clientStore.Set("juejin", &models.Client{ID: "juejin", Secret: "xxxxx", Domain: "http://juejin.com"}) |
||||||
|
|
||||||
|
// 设置 manager, manager 参与校验 code/access token 请求
|
||||||
|
manager = manage.NewDefaultManager() |
||||||
|
|
||||||
|
// 校验 redirect_uri 和 client 的 Domain, 简单起见, 不做校验
|
||||||
|
manager.SetValidateURIHandler(func(baseURI, redirectURI string) error { |
||||||
|
return nil |
||||||
|
}) |
||||||
|
|
||||||
|
manager.MustTokenStorage(store.NewMemoryTokenStore()) |
||||||
|
|
||||||
|
// manger 包含 client 信息
|
||||||
|
manager.MapClientStorage(clientStore) |
||||||
|
|
||||||
|
// 也包含 manger, client 信息
|
||||||
|
srv = server.NewServer(server.NewConfig(), manager) |
||||||
|
|
||||||
|
// 根据 client id 从 manager 中获取 client info, 在获取 access token 校验过程中会被用到
|
||||||
|
srv.SetClientInfoHandler(func(r *http.Request) (clientID, clientSecret string, err error) { |
||||||
|
clientInfo, err := srv.Manager.GetClient(r.Context(), r.URL.Query().Get("client_id")) |
||||||
|
if err != nil { |
||||||
|
log.Println(err) |
||||||
|
return "", "", err |
||||||
|
} |
||||||
|
return clientInfo.GetID(), clientInfo.GetSecret(), nil |
||||||
|
}) |
||||||
|
|
||||||
|
// 设置为 authorization code 模式
|
||||||
|
srv.SetAllowedGrantType(oauth2.AuthorizationCode) |
||||||
|
|
||||||
|
// authorization code 模式, 第一步获取code,然后再用code换取 access token, 而不是直接获取 access token
|
||||||
|
srv.SetAllowedResponseType(oauth2.Code) |
||||||
|
|
||||||
|
// 校验授权请求用户的handler, 会重定向到 登陆页面, 返回"", nil
|
||||||
|
srv.SetUserAuthorizationHandler(handleUserAuthorization) |
||||||
|
|
||||||
|
// 校验授权请求的用户的账号密码, 给 LoginHandler 使用, 简单起见, 只允许一个用户授权
|
||||||
|
srv.SetPasswordAuthorizationHandler(func(ctx context.Context, clientID, username, password string) (userID string, err error) { |
||||||
|
if username == "Tom" && password == "123456" { |
||||||
|
return "0001", nil |
||||||
|
} |
||||||
|
return "", errors.New("username or password error") |
||||||
|
}) |
||||||
|
|
||||||
|
// 允许使用 get 方法请求授权
|
||||||
|
srv.SetAllowGetAccessRequest(true) |
||||||
|
|
||||||
|
// 储存用户信息的一个 map
|
||||||
|
userInfoMap["0001"] = UserInfo{ |
||||||
|
"Tom", "Male", |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// HandleAuthorizeRequest 授权入口
|
||||||
|
func (c *Oauth2Controller) HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request) { |
||||||
|
err := srv.HandleAuthorizeRequest(w, r) |
||||||
|
if err != nil { |
||||||
|
log.Println(err) |
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// HandleLoginRequest 登陆入口
|
||||||
|
func (c *Oauth2Controller) HandleLoginRequest(w http.ResponseWriter, r *http.Request) { |
||||||
|
sessionStore, err := session.Start(r.Context(), w, r) |
||||||
|
if err != nil { |
||||||
|
log.Println(err) |
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if r.Method == http.MethodPost { |
||||||
|
userId, err := srv.PasswordAuthorizationHandler( |
||||||
|
r.Context(), |
||||||
|
r.FormValue("client_id"), |
||||||
|
r.FormValue("username"), |
||||||
|
r.FormValue("password"), |
||||||
|
) |
||||||
|
if err != nil { |
||||||
|
log.Println(err) |
||||||
|
http.Error(w, err.Error(), http.StatusUnauthorized) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// 保存登录状态
|
||||||
|
sessionStore.Set("LoggedInUserId", userId) |
||||||
|
sessionStore.Save() |
||||||
|
|
||||||
|
// 跳转到 同意授权页面
|
||||||
|
w.Header().Set("Location", "/oauth2/agree-auth") |
||||||
|
w.WriteHeader(http.StatusFound) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// 若请求方法错误, 提供login.html页面
|
||||||
|
c.Render(w, r, "static/login.html") |
||||||
|
} |
||||||
|
|
||||||
|
// HandleAgreeAuthRequest 同意授权的页面
|
||||||
|
func (c *Oauth2Controller) HandleAgreeAuthRequest(w http.ResponseWriter, r *http.Request) { |
||||||
|
store, err := session.Start(r.Context(), w, r) |
||||||
|
if err != nil { |
||||||
|
log.Println(err) |
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// 如果没有查询到登陆状态, 则跳转到 登陆页面
|
||||||
|
if _, ok := store.Get("LoggedInUserId"); !ok { |
||||||
|
w.Header().Set("Location", "/oauth2/login") |
||||||
|
w.WriteHeader(http.StatusFound) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// 如果有登陆状态, 会跳转到 确认授权页面
|
||||||
|
c.Render(w, r, "static/agree-auth.html") |
||||||
|
} |
||||||
|
|
||||||
|
// HandleAccessTokenRequest 使用 code 换取 access token
|
||||||
|
func (c *Oauth2Controller) HandleAccessTokenRequest(w http.ResponseWriter, r *http.Request) { |
||||||
|
err := srv.HandleTokenRequest(w, r) |
||||||
|
if err != nil { |
||||||
|
log.Println(err) |
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// HandleUserInfoRequest 使用 access_token 换取用户信息
|
||||||
|
func (c *Oauth2Controller) HandleUserInfoRequest(w http.ResponseWriter, r *http.Request) { |
||||||
|
// 获取 access token
|
||||||
|
accessToken, ok := srv.BearerAuth(r) |
||||||
|
if !ok { |
||||||
|
log.Println("Failed to get access token from request") |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
rootCtx := context.Background() |
||||||
|
ctx, cancelFunc := context.WithTimeout(rootCtx, time.Second) |
||||||
|
defer cancelFunc() |
||||||
|
|
||||||
|
// 从 access token 中获取 信息
|
||||||
|
tokenInfo, err := srv.Manager.LoadAccessToken(ctx, accessToken) |
||||||
|
if err != nil { |
||||||
|
log.Println(err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// 获取 user id
|
||||||
|
userId := tokenInfo.GetUserID() |
||||||
|
grantScope := tokenInfo.GetScope() |
||||||
|
|
||||||
|
userInfo := UserInfo{} |
||||||
|
|
||||||
|
// 根据 grant scope 决定获取哪些用户信息
|
||||||
|
if grantScope != "read_user_info" { |
||||||
|
log.Println("invalid grant scope") |
||||||
|
w.Write([]byte("invalid grant scope")) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
userInfo = userInfoMap[userId] |
||||||
|
resp, err := json.Marshal(userInfo) |
||||||
|
w.Write(resp) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// AuthorizeHandler 内部使用, 用于查看是否有登陆状态
|
||||||
|
func handleUserAuthorization(w http.ResponseWriter, r *http.Request) (userId string, err error) { |
||||||
|
store, err := session.Start(r.Context(), w, r) |
||||||
|
if err != nil { |
||||||
|
log.Println(err) |
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError) |
||||||
|
return |
||||||
|
} |
||||||
|
uid, ok := store.Get("LoggedInUserId") |
||||||
|
// 如果没有查询到登陆状态, 则跳转到登陆页面
|
||||||
|
if !ok { |
||||||
|
if r.Form == nil { |
||||||
|
r.ParseForm() |
||||||
|
} |
||||||
|
|
||||||
|
w.Header().Set("Location", "/oauth2/login") |
||||||
|
w.WriteHeader(http.StatusFound) |
||||||
|
return "", nil |
||||||
|
} |
||||||
|
// 若有登录状态, 返回 user id
|
||||||
|
userId = uid.(string) |
||||||
|
return userId, nil |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package entities |
||||||
|
|
||||||
|
import ( |
||||||
|
"gorm.io/gorm" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
type Client struct { |
||||||
|
Id uint `json:"id"` |
||||||
|
AccessKey string `json:"access_key" gorm:"primaryKey"` |
||||||
|
SecretKey string `json:"secret_key"` |
||||||
|
Domain string `json:"domain"` |
||||||
|
Public bool `json:"public"` |
||||||
|
CreatedAt time.Time `json:"create_time"` |
||||||
|
UpdatedAt time.Time `json:"update_time"` |
||||||
|
DeletedAt gorm.DeletedAt `json:"delete_time,omitempty" gorm:"index"` |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
package entities |
||||||
|
|
||||||
|
type UserData struct { |
||||||
|
Id uint `json:"id" gorm:"primaryKey"` |
||||||
|
Uid uint `json:"uid"` |
||||||
|
Nickname string `json:"nickname"` |
||||||
|
Gender string `json:"gender"` |
||||||
|
Avatar string `json:"avatar"` |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package entities |
||||||
|
|
||||||
|
import ( |
||||||
|
"database/sql" |
||||||
|
"gorm.io/gorm" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
type User struct { |
||||||
|
Id uint `json:"id" gorm:"primaryKey"` |
||||||
|
Nickname string `json:"secret_key"` |
||||||
|
Gender string `json:"gender"` |
||||||
|
Avatar string `json:"avatar"` |
||||||
|
Username sql.NullString `json:"username" gorm:"unique"` |
||||||
|
Email sql.NullString `json:"email" gorm:"unique"` |
||||||
|
PhoneNumber sql.NullString `json:"phone_number" gorm:"unique"` |
||||||
|
Password sql.NullString `json:"password"` |
||||||
|
Data []*UserData `json:"data,omitempty" gorm:"foreignKey:Uid;references:Id"` |
||||||
|
CreatedAt time.Time `json:"create_time"` |
||||||
|
UpdatedAt time.Time `json:"update_time"` |
||||||
|
DeletedAt gorm.DeletedAt `json:"delete_time,omitempty" gorm:"index"` |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
package oauth2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"errors" |
||||||
|
"github.com/go-oauth2/oauth2/v4" |
||||||
|
"github.com/go-oauth2/oauth2/v4/models" |
||||||
|
"gorm.io/gorm" |
||||||
|
"nucleus/internal/entities" |
||||||
|
) |
||||||
|
|
||||||
|
type ClientStore struct{} |
||||||
|
|
||||||
|
func (c *ClientStore) GetByID(ctx context.Context, id string) (oauth2.ClientInfo, error) { |
||||||
|
db, ok := ctx.Value("db").(*gorm.DB) |
||||||
|
if !ok { |
||||||
|
return nil, errors.New("missing database object") |
||||||
|
} |
||||||
|
|
||||||
|
var client entities.Client |
||||||
|
err := db.First(&client, "id=?", id).Error |
||||||
|
if err != nil { |
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) { |
||||||
|
return nil, errors.New("no client found") |
||||||
|
} |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
return &models.Client{ |
||||||
|
ID: client.AccessKey, |
||||||
|
Secret: client.SecretKey, |
||||||
|
Domain: client.Domain, |
||||||
|
Public: client.Public, |
||||||
|
//UserID: "",
|
||||||
|
}, nil |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
package oauth2 |
@ -0,0 +1,30 @@ |
|||||||
|
package oauth2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/go-oauth2/oauth2/v4" |
||||||
|
"github.com/go-oauth2/oauth2/v4/manage" |
||||||
|
"github.com/go-oauth2/oauth2/v4/server" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
manager *manage.Manager |
||||||
|
srv *server.Server |
||||||
|
) |
||||||
|
|
||||||
|
func Init() { |
||||||
|
manager = manage.NewDefaultManager() |
||||||
|
manager.SetValidateURIHandler(nil) |
||||||
|
//manager.MustTokenStorage()
|
||||||
|
manager.MapClientStorage(&ClientStore{}) |
||||||
|
|
||||||
|
srv = server.NewServer(server.NewConfig(), manager) |
||||||
|
srv.SetClientInfoHandler(nil) |
||||||
|
srv.SetAllowedGrantType(oauth2.AuthorizationCode) |
||||||
|
// authorization code 模式, 第一步获取code,然后再用code换取 access token, 而不是直接获取 access token
|
||||||
|
srv.SetAllowedResponseType(oauth2.Code) |
||||||
|
// 校验授权请求用户的handler, 会重定向到 登陆页面, 返回"", nil
|
||||||
|
srv.SetUserAuthorizationHandler(nil) |
||||||
|
// 校验授权请求的用户的账号密码, 给 LoginHandler 使用, 简单起见, 只允许一个用户授权
|
||||||
|
srv.SetPasswordAuthorizationHandler(nil) |
||||||
|
srv.SetAllowGetAccessRequest(true) |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
package oauth2 |
@ -0,0 +1,43 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"net/http" |
||||||
|
"nucleus/internal/controllers" |
||||||
|
) |
||||||
|
|
||||||
|
func main() { |
||||||
|
oauth2 := &controllers.Oauth2Controller{} |
||||||
|
|
||||||
|
oauth2.Init() |
||||||
|
|
||||||
|
// auth_server 授权入口
|
||||||
|
http.HandleFunc("/oauth2/authorize", oauth2.HandleAuthorizeRequest) |
||||||
|
|
||||||
|
// auth_server 发现未登录状态, 跳转到的登录handler
|
||||||
|
http.HandleFunc("/oauth2/login", oauth2.HandleLoginRequest) |
||||||
|
|
||||||
|
// auth_server拿到 client以后重定向到的地址, 也就是 auth_client 获取到了code, 准备用code换取access_token
|
||||||
|
//http.HandleFunc("/oauth2/code_to_token", internal.CodeToToken)
|
||||||
|
|
||||||
|
// auth_server 处理由code 换取access token 的handler
|
||||||
|
http.HandleFunc("/oauth2/token", oauth2.HandleAccessTokenRequest) |
||||||
|
|
||||||
|
// 登录完成, 同意授权的页面
|
||||||
|
http.HandleFunc("/oauth2/agree-auth", oauth2.HandleAgreeAuthRequest) |
||||||
|
|
||||||
|
// access token 换取用户信息的handler
|
||||||
|
http.HandleFunc("/oauth2/userinfo", oauth2.HandleUserInfoRequest) |
||||||
|
|
||||||
|
http.Handle("/", http.FileServer(http.Dir("./static"))) |
||||||
|
|
||||||
|
errChan := make(chan error) |
||||||
|
go func() { |
||||||
|
errChan <- http.ListenAndServe(":9001", nil) |
||||||
|
}() |
||||||
|
err := <-errChan |
||||||
|
if err != nil { |
||||||
|
fmt.Println("Hello internal stop running.") |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<title>Title</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<form action="http://localhost:9001/oauth2/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A9001%2Fcode-to-user-info.html&response_type=code&client_id=juejin&scope=read_user_info" method="post"> |
||||||
|
<button type="submit" class="btn btn-primary btn-lg" style="width: 200px">同意授权</button> |
||||||
|
</form> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,63 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<title>Title</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<script> |
||||||
|
// 一个同步的http请求 |
||||||
|
function httpRequest(address, reqType, asyncProc) { |
||||||
|
var req = new XMLHttpRequest(); |
||||||
|
if (asyncProc) { |
||||||
|
req.onreadystatechange = function() { |
||||||
|
if (this.readyState === 4) { |
||||||
|
asyncProc(this); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
req.open(reqType, address, !(!asyncProc)); |
||||||
|
req.send(); |
||||||
|
return req; |
||||||
|
} |
||||||
|
|
||||||
|
// 获取 code 参数 |
||||||
|
var query = decodeURI(window.location.search.substring(1)); |
||||||
|
var vars = query.split("&"); |
||||||
|
var code ='' |
||||||
|
for (var i = 0; i < vars.length; i++) { |
||||||
|
var pair = vars[i].split("="); |
||||||
|
if (pair[0] == "code") { |
||||||
|
console.log("code = ",pair[1]) |
||||||
|
code=pair[1] |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// code 换取 access token |
||||||
|
let access_token; |
||||||
|
let token_url = 'http://localhost:9001/oauth2/token?code={Code}&grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A9001%2Fcode-to-user-info.html&client_id=juejin'; |
||||||
|
token_url =token_url.replace('{Code}',code) |
||||||
|
console.log("token_url = ",token_url) |
||||||
|
const req1 = httpRequest(token_url, "Get", false); |
||||||
|
if ( req1.status == 200) { |
||||||
|
console.log(req1.response ) |
||||||
|
} |
||||||
|
let token_data =JSON.parse(req1.response) |
||||||
|
access_token = token_data["access_token"] |
||||||
|
|
||||||
|
// access_token 换取用户信息 |
||||||
|
let user_info_url = 'http://localhost:9001/oauth2/userinfo?access_token={AccessToken}'; |
||||||
|
user_info_url =user_info_url.replace('{AccessToken}',access_token) |
||||||
|
console.log("user_info_url = ",user_info_url) |
||||||
|
|
||||||
|
const req2 = httpRequest(user_info_url, "Get", false); |
||||||
|
if ( req2.status === 200) { |
||||||
|
console.log("user info = " ,req2.response ) |
||||||
|
} |
||||||
|
|
||||||
|
alert( req2.response) |
||||||
|
</script> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,12 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<title>Title</title> |
||||||
|
</head> |
||||||
|
|
||||||
|
<body> |
||||||
|
<H1>我的网站</H1> |
||||||
|
<input type="button" value="使用 Nucleus 登录" onclick="window.location.href='http://localhost:9001/oauth2/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fcode-to-user-info.html&response_type=code&client_id=juejin&scope=read_user_info'" /> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,43 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<title>Title</title> |
||||||
|
<style> |
||||||
|
form { |
||||||
|
width: 280px; |
||||||
|
margin: 45px auto; |
||||||
|
} |
||||||
|
fieldset { |
||||||
|
background: antiquewhite; |
||||||
|
} |
||||||
|
.p12 {padding: 24px} |
||||||
|
.mb12 {margin-bottom: 12px} |
||||||
|
</style> |
||||||
|
</head> |
||||||
|
|
||||||
|
<body> |
||||||
|
<form action="/oauth2/login" method="post"> |
||||||
|
<fieldset class="p12 mb12"> |
||||||
|
<legend>登陆</legend> |
||||||
|
<div class="mb12"> |
||||||
|
<label for="username">用户名/手机号码/邮箱地址</label> |
||||||
|
<input type="text" name="username" id="username"> |
||||||
|
</div> |
||||||
|
<div class="mb12"> |
||||||
|
<div> |
||||||
|
<label for="password">登录密码</label> |
||||||
|
<a href="#">忘记密码</a> |
||||||
|
</div> |
||||||
|
<input class="w100" type="password" name="password" id="password"> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<button type="submit">登陆</button> |
||||||
|
</div> |
||||||
|
</fieldset> |
||||||
|
<div> |
||||||
|
新用户登陆?<a href="#">创建账号</a> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
</body> |
||||||
|
</html> |
Loading…
Reference in new issue