内容简介:版权声明:本文为博主原屙文章,喜欢你就担走。 https://blog.csdn.net/leftfist/article/details/84630320
版权声明:本文为博主原屙文章,喜欢你就担走。 https://blog.csdn.net/leftfist/article/details/84630320
网上有高手开源了一个网盘项目: 蓝眼云盘 ,我一看还行,版权也很宽松,是 MIT ,就用到了项目里面去。
有个问题就是我们项目采用了CAS作为单点登录,而这个蓝眼云盘有自己的一套登录机制。需要改造一下,将单点登录也集成到云盘中来。
蓝眼云盘项目服务器端是用 GO 语言开发的,前端则用了VUE.JS框架,这两样我都没接触过。而且成品的前端还用了webpack进行打包,很难看清。webpack也没接触过。一片空白啊。
赶鸭子上架,花了约2周的时间,尽管还不完美,但终于像点样子了。
思路或步骤记录如下:
一、要有一个CAS for Go语言的客户端
这个客户端做什么用呢?接管和控制用户请求,发现未经过CAS认证就转向CAS。
这个CAS客户端当然也是golang开发的。
源代码请狠狠点击 这里
下载,编译以后,可以运行_examples/cas_chi.go(命令行方式下,go run cas_chi.go),就能看到这个客户端的效果了:在浏览器输入 localhost:9999,会先跳到CAS的登录页。
但是!官方代码有个坑,就是登录以后,会出现重定向次数过多的错误。有人修复了这个坑。修改过的_examples/cas_chi.go代码如下:
package main
import (
"bytes"
"fmt"
"html/template"
"log"
"net/http"
"net/url"
"github.com/go-chi/chi"
"github.com/shenshouer/cas"
)
var casURL = "http://192.168.0.22:8080/cas2/" //单点登录地址
type templateBinding struct {
Username string
Attributes cas.UserAttributes
}
func main() {
url, _ := url.Parse(casURL)
client := cas.NewClient(&cas.Options{URL: url})
root := chi.NewRouter()
root.Use(client.Handler)
//这句为新增代码
server := &http.Server{
Addr: ":9999",
Handler: client.Handle(root),
}
root.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/html")
tmpl, err := template.New("index.html").Parse(index_html)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, error_500, err)
return
}
binding := &templateBinding{
Username: cas.Username(r),
Attributes: cas.Attributes(r),
}
html := new(bytes.Buffer)
if err := tmpl.Execute(html, binding); err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, error_500, err)
return
}
html.WriteTo(w)
})
//if err := http.ListenAndServe(":9999", root); err != nil { //屏蔽原先这句代码
if err := server.ListenAndServe(); err != nil {//改为这句
log.Fatal(err)
//fmt.Println("error!")
}
//}
}
const index_html = `<!DOCTYPE html>
<html>
<head>
<title>Welcome {{.Username}}</title>
</head>
<body>
<h1>Welcome {{.Username}} <a href="/logout">Logout</a></h1>
<p>Your attributes are:</p>
<ul>{{range $key, $values := .Attributes}}
<li>{{$len := len $values}}{{$key}}:{{if gt $len 1}}
<ul>{{range $values}}
<li>{{.}}</li>{{end}}
</ul>
{{else}} {{index $values 0}}{{end}}</li>{{end}}
</ul>
</body>
</html>
`
const error_500 = `<!DOCTYPE html>
<html>
<head>
<title>Error 500</title>
</head>
<body>
<h1>Error 500</h1>
<p>%v</p>
</body>
</html>
`
二、以这个 _examples/cas_chi.go 为蓝本,可以移植到蓝眼云盘了。
golang的项目必有一个main包,main包里必有一个main函数。云盘初始化之际,执行main函数,就是植入CAS客户端之时。
思路是这样子的:
云盘初始化的时候,改用CAS客户端接管和监听服务请求。
具体如何处理请求呢?
CAS客户端有个最大的特点,就是如果尚未进行认证,就一定会先转向单点登录;认证过,才会到达我们自己写的这些代码。
好,现在到达我们代码部分,每个请求过来的时候,先判断一下是否需要进行自动登录。如果不需要,就按照云盘设定的路由规则执行,否则转向自动登录页面。自动登录页面,顾名思义,会调用自动登录路由进行云盘登录。
如何判断是否需要进行自动登录呢?
是静态文件吗?,如JS,CSS,图片之类,是的话,肯定不需要登录。
否则检查cookie,如果cookie有相关信息,则已经登录过,也不需要再登录。
一系列判断之后,才输出自动登录页面。输出的时候,会将从CAS客户端获取到的登录账号一并输出。
如前所述,在登录页面,会用ajax采取POST的方式到路由:api/user/autoLogin。这个路由是俺新开发的,有如下功效:
根据提交上来的账号名称,到数据库中读取,如果能获取,登录顺利通过;如果没有,则创建一个,然后登录通过。(可以发现,这里面有些安全性问题,怎么保证提交上来的账号就是单点登录认证过的哪个账号?)
修改过的云盘main函数代码如下:
//main.gofunc main() {
//将运行时参数装填到config中去。
rest.PrepareConfigs()
context := rest.NewContext()
defer context.Destroy()
//http.Handle("/", context.Router)//屏蔽了原先的路由
dotPort := fmt.Sprintf(":%v", rest.CONFIG.ServerPort)
info := fmt.Sprintf("App started at http://localhost%v", dotPort)
rest.LogInfo(info)
fmt.Println("网盘运行中:http://localhost%v,请勿关闭",dotPort)
rest.CasDog(dotPort,context)//人来落闸放狗,CAS客户端接管了服务监听
/* err := http.ListenAndServe(dotPort, nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}*/
}
“CAS狗”文件:
rest/cas_chi.go
package rest
import (
"bytes"
"fmt"
"html/template"
"github.com/go-chi/chi"
"github.com/shenshouer/cas"
"log"
"net/http"
"net/url"
"strings"
)
type templateBinding struct {
Account string
}
func CasDog(port string,ctx *Context) {
url, _ := url.Parse(CONFIG.Cas)
client := cas.NewClient(&cas.Options{URL: url})
root := chi.NewRouter()
root.Use(client.Handler)
server := &http.Server{
Addr: port,
Handler: client.Handle(root),
}
root.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) {//注意路由规则是“/*”,而不是"/"
//否则静态文件等无法访问
if !autoLogin(ctx,w,r){//如果无须转向自动登录页面,就按照原先的路由规则执行
ctx.Router.ServeHTTP(w,r)
}
})
if err := server.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
func autoLogin(ctx *Context,w http.ResponseWriter,r *http.Request) bool {//自动登录
path := r.URL.Path
if strings.Index(path, "/autoLogin") >= 0 || isStaticFile(r){
return false
}
cookie, _ := r.Cookie(COOKIE_AUTH_KEY)
if cookie != nil{
ar := strings.Split(cookie.Value,"|")
if len(ar) > 1{
username := ar[1]
if username == cas.Username(r){
return false
}
}
}
outputAutoLoginPage(w,r)
return true
}
func isStaticFile(r *http.Request) bool{//是静态资源吗
suffixs := [6]string{".js",".css",".png",".jpg",".gif",".html"}
for _,sf := range suffixs{
if strings.HasSuffix(strings.ToLower(r.URL.Path),sf){
return true
}
}
return false
}
func outputAutoLoginPage(w http.ResponseWriter, r *http.Request){//输出自动登录页面
w.Header().Add("Content-Type", "text/html")
tmpl, err := template.New("autoLogin.html").Parse(index_html)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, error_500, err)
return
}
binding := &templateBinding{
Account: cas.Username(r),
}
html := new(bytes.Buffer)
if err := tmpl.Execute(html, binding); err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, error_500, err)
return
}
html.WriteTo(w)
}
const index_html = `<!DOCTYPE html>
<html>
<head>
<title>Welcome {{.Account}}</title>
<script src="/static/dist/jquery-1.8.3.min.js"></script>
<script src="/static/dist/auto.js"></script>
</head>
<body>
<div>login,please wait....</div>
</body>
</html>
<script>
autoLogin('/api/user/autoLogin','name={{.Account}}');
</script>
`
const error_500 = `<!DOCTYPE html>
<html>
<head>
<title>Error 500</title>
</head>
<body>
<h1>Error 500</h1>
<p>%v</p>
</body>
</html>
`
//自动登录路由
user_controller.go
//注册一个路由
routeMap["/api/user/autoLogin"] = this.Wrap(this.AutoLogin, USER_ROLE_GUEST)
。。。
func (this *UserController) AutoLogin(writer http.ResponseWriter, request *http.Request) *WebResult {
name := request.FormValue("name")
user := this.userDao.FindByUserName(name)//新加的方法,用账号名来获取用户信息
if user == nil {
return this.autoCreate(name,writer,request)//自动创建
} else {
return this.loginImpl(user,writer,request)//登录实现
}
}
以上详细代码就不贴了,容易实现
三、自动登录页面
这里要介绍一下蓝眼云盘的登录机制。它并不是完全依赖cookie的。貌似cookie,只是它的后端使用;而前端则完全依赖于local storage。所以在自动登录成功后,需要将登录信息写入local storage。
如前所述,自动登录页面是后端输出的,除了引用的js
const index_html = `<!DOCTYPE html>
<html>
<head>
<title>Welcome {{.Account}}</title>
<script src="/static/dist/jquery-1.8.3.min.js"></script>
<script src="/static/dist/auto.js"></script>
</head>
<body>
<div>login,please wait....</div>
</body>
</html>
<script>
autoLogin('/api/user/autoLogin','name={{.Account}}');
</script>
`
/static/dist/auto.js
function autoLogin(url,data){
$.ajax({
type: 'post',
url: url,
contentType: "application/x-www-form-urlencoded; charset=utf-8",
dataType: "json",
data:data,
timeout: 30000,
success: function (msg) {
msg.data.isLogin = true;
let json = JSON.stringify(msg.data);
let key = "user";
window.localStorage.removeItem(key);
window.localStorage.setItem(key,json);//将信息写入local storage
location.href = "/";
},
error: function (err) {
alert(err.responseText);
}
});
}
四、遗留问题
单点登出没解决。就是别的系统已经登出了,但云盘这里没反应,仍然处于登录状态。这在别的系统退出并切换账号登录时,就发现两边账号不一致。本来大家都是张三,现在一个是张三,一个是李四。
以上所述就是小编给大家介绍的《Go语言项目集成CAS单点登录》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Django - 集成CAS单点登录
- JIRA 集成单点登录系统 CAS
- 只需要 6 个步骤,Spring Boot 集成 shiro,并完成登录
- JustAuth 1.10.0 发布,集成华为和企业微信登录,更加灵活的state缓存
- 使用node.js和oAuth2协议集成Github/LinkedIn第三方登录以OnceOA模块源码为例
- beego实现用户未登录跳转到登录页面
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深入浅出MFC (第二版)
侯俊杰 / 华中科技大学出版社 / 2001-1 / 80.00元
《深入浅出MFC》分为四大篇。第一篇提出学习MFC程序设计之前的必要基础,包括Widnows程序的基本观念以及C++的高阶议题。“学前基础”是相当主观的认定,但作者是甚于自己的学习经验以及教学经验,其挑选应该颇具说服力。第二篇介绍Visual C++整合环境开发工具。此篇只是提纲挈领,并不企图取代Visual C++使用手册;然而对于软件使用的老手,此篇或已足以帮助掌握Visual C++整合环境......一起来看看 《深入浅出MFC (第二版)》 这本书的介绍吧!