There is a master open source on the net disk project: blue eye cloud disk, I see also, copyright is very loose, is MIT, with the project inside to go.

One problem is that our project adopts CAS as single sign-on, and this blue Eye cloud disk has its own login mechanism. Need a revamp to integrate single sign-on into the cloud disk as well.

The server side of blue Eye Cloud Disk project is developed with GO language, and the front end uses vue.js framework, which I have never come into contact with. And the front end of the finished product is also packed with Webpack, it is difficult to see. Webpack didn’t touch it either. It’s a blank.

It took about two weeks to get the ducks on the shelves, and although it’s not perfect, it looks good.

The ideas or steps are recorded as follows:

First, to have a CAS for Go language client, this is part of the preparatory work

What does this client do? Take over and control user requests, and discover that they are redirected to CAS without CAS authentication. The CAS client was also developed by Golang, of course. Click here for the source code

Go (command line, go run cas_chi. Go) to see the effect of the client: Enter localhost:9999 in the browser and the CAS login page will be displayed first.

But! There is a pit in the official code, that is, after login, there will be too many redirection errors. Someone repaired the hole. The modified _examples/cas_chi.go code is as follows:

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/" // Sso address

type templateBinding struct {
	Username   string
	Attributes cas.UserAttributes
}

func main(a) {
	url, _ := url.Parse(casURL)
	client := cas.NewClient(&cas.Options{URL: url})

	root := chi.NewRouter()
	root.Use(client.Handler)

	// This is the new code
	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)

		iferr ! =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)
		iferr := 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 {// mask the original code
		iferr := server.ListenAndServe(); err ! =nil {// Change to this sentence
			log.Fatal(err)
			//fmt.Println("error!" )
		}
	/ /}
}

const index_html = ` 
         Welcome {{.Username}}   

Welcome {{.Username}} Logout

Your attributes are:

    {{range $key, $values := .Attributes}}
  • {{$len := len $values}}{{$key}}:{{if gt $len 1}}
      {{range $values}}
    • {{.}}
    • {{end}}
    {{else}} {{index $values 0}}{{end}}
  • {{end}}
`
const error_500 = ` Error 500

Error 500

%v

`
Copy the code

2. Modify the main function of the cloud disk by referring to the principle of _examples/cas_chi.go

Golang projects must have a main package, and the main package must have a main function. When the cloud disk is initialized, run the main function to implant the CAS client.

Here’s the idea:

During cloud disk initialization, the CAS client takes over and listens to service requests.

How exactly do you handle requests? The biggest feature of the CAS client is that it must switch to single sign-on (SSO) before authentication. Authentication will arrive at the code that we wrote ourselves. This is all controlled by the CAS client.

Okay, now that we get to our code, when each request comes in, determine whether automatic login is required. If no, follow the route rules set on the cloud disk. If no, switch to the automatic login page.

** How to determine whether automatic login is required? Is it a static file? , such as JS, CSS, images and so on, if yes, certainly do not need to log in. Otherwise, check the cookie. If the cookie has relevant information, you have logged in and do not need to log in again. After a series of judgments, the automatic login page is output. The system outputs the login account obtained by the CAS client.

The modified main function code is as follows: //main.go

func main(a) {

	// Load run-time parameters into config.
	rest.PrepareConfigs()
	context := rest.NewContext()
	defer context.Destroy()

	// http.handle ("/", context.router)// Mask the original route

	dotPort := fmt.Sprintf(":%v", rest.CONFIG.ServerPort)

	info := fmt.Sprintf("App started at http://localhost%v", dotPort)
	rest.LogInfo(info)

	fmt.Println("Web disk in operation: http://localhost%v, do not close",dotPort)

	rest.CasDog(dotPort,context)// The CAS client takes over the service listening

/* err := http.ListenAndServe(dotPort, nil) if err ! = nil { log.Fatal("ListenAndServe: ", err) }*/
}
Copy the code

“CAS dog” file: 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) {// Note that the routing rule is "/ * "instead of "/".
	// Otherwise, static files cannot be accessed
		if! autoLogin(ctx,w,r){// If there is no need to go to the automatic login page, follow the original routing rules
			ctx.Router.ServeHTTP(w,r)
		}
	})

	iferr := server.ListenAndServe(); err ! =nil {
		log.Fatal(err)
	}
}
func autoLogin(ctx *Context,w http.ResponseWriter,r *http.Request) bool {// Automatic login
	path := r.URL.Path
	if strings.Index(path, "/autoLogin") > =0 || isStaticFile(r){
		return false
	}

	cookie, _ := r.Cookie(COOKIE_AUTH_KEY)
	ifcookie ! =nil{
		ar := strings.Split(cookie.Value,"|")
		if len(ar) > 1{
			username := ar[1]
			if username == cas.Username(r){// The user has logged in to the cloud disk and has related cookie information
				return false
			}
		}
	}

	outputAutoLoginPage(w,r)
	return true
}
func isStaticFile(r *http.Request) bool{Is it a static resource
	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){// Output the automatic login page
	w.Header().Add("Content-Type"."text/html")

	tmpl, err := template.New("autoLogin.html").Parse(index_html)

	iferr ! =nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, error_500, err)
		return
	}

	binding := &templateBinding{
		Account:   cas.Username(r),// Account used to log in to CAS
	}

	html := new(bytes.Buffer)
	iferr := tmpl.Execute(html, binding); err ! =nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, error_500, err)
		return
	}

	html.WriteTo(w)
}

const index_html = ` 
      < HTML >< head> Welcome {{.account}}      
      
login,please wait....
'
const error_500 = ` Error 500

Error 500

%v

`
Copy the code

Automatic login If you have been authenticated by the CENTRAL Authentication Service (CAS), you must automatically log in to the cloud disk for the first time. The modification includes two parts: one is to register an automatic login route on the server side, and the other is to add an automatic login page on the client side.

1. Automatic login Route Is responsible for automatic login of the account sent back from CAS on the cloud disk. If the account does not exist, you need to create it before automatic login. This function is written on the server side and registered as a route, open to automatic login page calls.

Change the value in user_Controller.go

// Register a route
routeMap["/api/user/autoLogin"AutoLogin (this.autologin, USER_ROLE_GUEST) = 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)// Add a new method to get user information by using the account name
	if user == nil {
		return this.autoCreate(name,writer,request)// Create it automatically
	} else {
		return this.loginImpl(user,writer,request)// Login implementation}}Copy the code

The above detailed code is not posted, easy to achieve

2. Automatic login Page This page is responsible for submitting account information to the automatic login route.

Originally, when the CAS is transferred back to the cloud disk, the account information is stored on the server side. Why not directly process the information on the server side, but output the information to the page and then submit it back to the server? I’ve come a long way. The reason is not easy to change, the original cloud disk structure is the client to call the server API, login is also, so for the time being.

The main page is output in the main function of the cloud disk, and the js of the external chain is placed in /static/dist/ auto-.js

Here is to introduce the blue eye cloud disk login mechanism. It’s not entirely cookie dependent. Looks like a cookie, just its backend use; The front-end is completely dependent on local storage. Therefore, after the automatic login succeeds, you need to write the login information to the local storage.

const index_html = `<! DOCTYPEhtml>
<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>
`
Copy the code

/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);// Write the information to the local storage
			location.href = "/";
		},
		error: function (err) { alert(err.responseText); }}); }Copy the code

4. Solve the remaining problems by single spot boarding. Is that other systems have been logged out, but there is no response here cloud disk, still in the login state. When other systems log out and switch accounts, the two accounts are found to be inconsistent. We used to be all Three, now one is three, one is Four.