瀏覽代碼

Add login to applicatio

Toni Fadjukoff 6 年之前
父節點
當前提交
5c1d27c5cd
共有 3 個文件被更改,包括 151 次插入50 次删除
  1. 74 0
      auth.go
  2. 55 16
      web.go
  3. 22 34
      web/index.html

+ 74 - 0
auth.go 查看文件

@@ -0,0 +1,74 @@
1
+package main
2
+
3
+import (
4
+	"crypto/rand"
5
+	"encoding/base64"
6
+	"errors"
7
+	"log"
8
+	"net/http"
9
+	"time"
10
+)
11
+
12
+type SessionData struct {
13
+	username string
14
+}
15
+
16
+var (
17
+	sessions = make(map[string]*SessionData)
18
+)
19
+
20
+func userOk(username, password string) bool {
21
+	return (username == "Lamperi" && password == "paskaa")
22
+}
23
+
24
+func tryLogin(username, password string, longerTime bool) (http.Cookie, error) {
25
+	if exists := userOk(username, password); !exists {
26
+		return http.Cookie{},
27
+			errors.New("The username or password you entered isn't correct.")
28
+	}
29
+
30
+	sid, err := randString(32)
31
+	if err != nil {
32
+		return http.Cookie{}, err
33
+	}
34
+
35
+	sessions[sid] = &SessionData{username}
36
+
37
+	hours := time.Duration(1)
38
+	if longerTime {
39
+		hours = time.Duration(336)
40
+	}
41
+
42
+	loginCookie := http.Cookie{
43
+		Name:     "id",
44
+		Value:    sid,
45
+		MaxAge:   int((time.Hour * hours).Seconds()),
46
+		HttpOnly: true,
47
+	}
48
+
49
+	return loginCookie, nil
50
+}
51
+
52
+func getSession(req *http.Request) (*SessionData, error) {
53
+	cookie, err := req.Cookie("id")
54
+	if err != nil {
55
+		return nil, err
56
+	}
57
+
58
+	session, exists := sessions[cookie.Value]
59
+	if !exists {
60
+		return nil, errors.New("Session expired from server")
61
+	}
62
+	return session, nil
63
+}
64
+
65
+func randString(size int) (string, error) {
66
+	buf := make([]byte, size)
67
+
68
+	if _, err := rand.Read(buf); err != nil {
69
+		log.Println(err)
70
+		return "", errors.New("Couldn't generate random string")
71
+	}
72
+
73
+	return base64.URLEncoding.EncodeToString(buf)[:size], nil
74
+}

+ 55 - 16
web.go 查看文件

@@ -17,26 +17,39 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
17 17
 		return
18 18
 	}
19 19
 
20
-	songs, err := loadDb()
21
-	if err != nil {
22
-		http.Error(w, err.Error(), http.StatusInternalServerError)
23
-		return
24
-	}
25 20
 	alsoEmptySongs := SongsFile{
26 21
 		Songs: make([]*SongEntry, 52),
27 22
 	}
28 23
 	for week := 1; week <= 52; week++ {
29 24
 		alsoEmptySongs.Songs[week-1] = &SongEntry{Week: week}
30 25
 	}
31
-	for _, song := range songs.Songs {
32
-		alsoEmptySongs.Songs[song.Week-1] = song
26
+	username := ""
27
+	session, err := getSession(r)
28
+	if session != nil {
29
+		songs, err := loadDb()
30
+		if err != nil {
31
+			http.Error(w, err.Error(), http.StatusInternalServerError)
32
+			return
33
+		}
34
+		for _, song := range songs.Songs {
35
+			alsoEmptySongs.Songs[song.Week-1] = song
36
+		}
37
+		username = session.username
33 38
 	}
34 39
 
35 40
 	var templates = cachedTemplates
36 41
 	if true {
37 42
 		templates = template.Must(template.ParseFiles("web/index.html"))
38 43
 	}
39
-	err = templates.ExecuteTemplate(w, "index.html", alsoEmptySongs)
44
+
45
+	data := struct {
46
+		Username string
47
+		Songs    []*SongEntry
48
+	}{
49
+		username,
50
+		alsoEmptySongs.Songs,
51
+	}
52
+	err = templates.ExecuteTemplate(w, "index.html", data)
40 53
 	if err != nil {
41 54
 		http.Error(w, err.Error(), http.StatusInternalServerError)
42 55
 	}
@@ -47,6 +60,7 @@ func updateHandler(w http.ResponseWriter, r *http.Request) {
47 60
 		http.Error(w, "Forbidden", http.StatusForbidden)
48 61
 		return
49 62
 	}
63
+
50 64
 	err := r.ParseForm()
51 65
 	if err != nil {
52 66
 		http.Error(w, err.Error(), http.StatusBadRequest)
@@ -100,16 +114,41 @@ func updateHandler(w http.ResponseWriter, r *http.Request) {
100 114
 	http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
101 115
 }
102 116
 
117
+func loginHandler(w http.ResponseWriter, r *http.Request) {
118
+	err := r.ParseForm()
119
+	if err != nil {
120
+		http.Error(w, err.Error(), http.StatusBadRequest)
121
+		return
122
+	}
123
+	username := r.Form.Get("username")
124
+	password := r.Form.Get("password")
125
+	remember := r.Form.Get("remember")
126
+	longer := remember == "remember-me"
127
+
128
+	cookie, err := tryLogin(username, password, longer)
129
+
130
+	if err != nil {
131
+		http.Error(w, err.Error(), http.StatusForbidden)
132
+		return
133
+	}
134
+
135
+	http.SetCookie(w, &cookie)
136
+	http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
137
+}
138
+
103 139
 func webStart(modifiedSongChan chan *SongEntry) {
104 140
 	songsChan = modifiedSongChan
105 141
 
142
+	mux := http.NewServeMux()
143
+
106 144
 	fs := http.FileServer(http.Dir("web"))
107
-	http.Handle("/css/", fs)
108
-	http.Handle("/fonts/", fs)
109
-	http.Handle("/js/", fs)
110
-	http.Handle("/favicon.ico", fs)
111
-	http.HandleFunc("/", indexHandler)
112
-	http.HandleFunc("/update", updateHandler)
113
-
114
-	http.ListenAndServe("localhost:8080", nil)
145
+	mux.Handle("/css/", fs)
146
+	mux.Handle("/fonts/", fs)
147
+	mux.Handle("/js/", fs)
148
+	mux.Handle("/favicon.ico", fs)
149
+	mux.HandleFunc("/", indexHandler)
150
+	mux.HandleFunc("/update", updateHandler)
151
+	mux.HandleFunc("/login", loginHandler)
152
+
153
+	http.ListenAndServe("localhost:8080", mux)
115 154
 }

+ 22 - 34
web/index.html 查看文件

@@ -18,48 +18,21 @@
18 18
 
19 19
   <body>
20 20
 
21
-    <nav class="navbar navbar-toggleable-md navbar-inverse fixed-top bg-inverse">
22
-      <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
23
-        <span class="navbar-toggler-icon"></span>
24
-      </button>
25
-      <a class="navbar-brand" href="#"></a>
26
-
27
-      <div class="collapse navbar-collapse" id="navbarsExampleDefault">
28
-        <ul class="navbar-nav mr-auto">
29
-          <li class="nav-item active">
30
-            <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
31
-          </li>
32
-          <li class="nav-item">
33
-            <a class="nav-link" href="#">Link</a>
34
-          </li>
35
-          <li class="nav-item">
36
-            <a class="nav-link disabled" href="#">Disabled</a>
37
-          </li>
38
-          <li class="nav-item dropdown">
39
-            <a class="nav-link dropdown-toggle" href="http://example.com" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
40
-            <div class="dropdown-menu" aria-labelledby="dropdown01">
41
-              <a class="dropdown-item" href="#">Action</a>
42
-              <a class="dropdown-item" href="#">Another action</a>
43
-              <a class="dropdown-item" href="#">Something else here</a>
44
-            </div>
45
-          </li>
46
-        </ul>
47
-        <form class="form-inline my-2 my-lg-0">
48
-          <input class="form-control mr-sm-2" type="text" placeholder="Search">
49
-          <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
50
-        </form>
51
-      </div>
52
-    </nav>
53
-
54 21
     <!-- Main jumbotron for a primary marketing message or call to action -->
55 22
     <div class="jumbotron">
56 23
       <div class="container">
57
-        <h1 class="display-3">Hello Lamperi!</h1>
24
+        {{if .Username}}
25
+        <h1 class="display-3">Hello {{ .Username }}!</h1>
58 26
         <p>Please fill in the songs for year 2018.</p>
27
+        {{else}}
28
+        <h1 class="display-3">Please log in</h1>
29
+        <p>Login to fill in the songs for year 2018.</p>
30
+        {{end}}
59 31
       </div>
60 32
     </div>
61 33
 
62 34
     <div class="container">
35
+        {{if .Username }}
63 36
         {{range .Songs }}
64 37
         <div class="row">
65 38
             <small style="width: 70px">Week {{ .Week }}</small>
@@ -84,6 +57,21 @@
84 57
             
85 58
         </div>
86 59
         {{end}}
60
+        {{else}}
61
+        <form class="form-signin" method="post" action="/login">
62
+            <h2 class="form-signin-heading">Please sign in</h2>
63
+            <label for="username" class="sr-only">Email address</label>
64
+            <input type="text" name="username" class="form-control" placeholder="Username" required autofocus>
65
+            <label for="password" class="sr-only">Password</label>
66
+            <input type="password" name="password" class="form-control" placeholder="Password" required>
67
+            <div class="checkbox">
68
+                <label>
69
+                    <input type="checkbox" name="remember-me" value="remember-me"> Remember me
70
+                </label>
71
+            </div>
72
+            <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
73
+        </form>
74
+    {{end}}
87 75
 
88 76
       <hr>
89 77