-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit bf632e2
Showing
35 changed files
with
1,265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
test-results/ | ||
tmp/ | ||
routes/ | ||
*.db |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# deer-api | ||
An API server using JWT Authentication based on Revel framework. | ||
|
||
|
||
### About JWT Authetication | ||
|
||
Using jwt-go lib. Setting user's email as `email` in cliams. | ||
|
||
It behaviors like knock ruby gem. https://github.com/nsarno/knock | ||
|
||
### Authenticating from a web or mobile application | ||
|
||
1. register user | ||
``` | ||
curl -v -X POST -d 'email="test@test.com"&password="your_pass"' http://localhost:9001/register | ||
``` | ||
|
||
the response | ||
``` | ||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im5ldHF5cUAxNjMuY29tIiwibmJmIjoxNDQ0NDc4NDAwfQ.bZo1DzrzZBetB9IP7fVip5XA_GiFBb_z8zDNTalReuU" | ||
``` | ||
|
||
|
||
2. request to get a token from your API: | ||
``` | ||
POST /login | ||
{"auth": {"email": "foo@bar.com", "password": "secret"}} | ||
``` | ||
|
||
Example response from the API: | ||
``` | ||
201 Created | ||
{"jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"} | ||
``` | ||
|
||
To make an authenticated request to your API, you need to pass the token via the request header: | ||
``` | ||
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 | ||
GET /my_resources | ||
``` | ||
|
||
|
||
### Basic CRUD Example | ||
`resources :products` | ||
|
||
create product | ||
``` | ||
curl -v -X POST -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im5ldHF5cUAxNjMuY29tIiwibmJmIjoxNDQ0NDc4NDAwfQ.bZo1DzrzZBetB9IP7fVip5XA_GiFBb_z8zDNTalReuU" -d 'name="tomato"&price="12.0"&code="T0001"}' "http://localhost:9001/products" | ||
``` | ||
|
||
show product | ||
``` | ||
curl -v -X GET -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im5ldHF5cUAxNjMuY29tIiwibmJmIjoxNDQ0NDc4NDAwfQ.bZo1DzrzZBetB9IP7fVip5XA_GiFBb_z8zDNTalReuU" "http://localhost:9001/products/1" | ||
``` | ||
|
||
index product | ||
``` | ||
curl -v -X GET -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im5ldHF5cUAxNjMuY29tIiwibmJmIjoxNDQ0NDc4NDAwfQ.bZo1DzrzZBetB9IP7fVip5XA_GiFBb_z8zDNTalReuU" "http://localhost:9001/products" | ||
``` | ||
|
||
update product | ||
``` | ||
curl -v -X PUT -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im5ldHF5cUAxNjMuY29tIiwibmJmIjoxNDQ0NDc4NDAwfQ.bZo1DzrzZBetB9IP7fVip5XA_GiFBb_z8zDNTalReuU" -d 'name="tomato"&price="12.0"&code="T0001"}' "http://localhost:9001/products/1" | ||
``` | ||
|
||
delete product | ||
``` | ||
curl -v -X DELETE -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im5ldHF5cUAxNjMuY29tIiwibmJmIjoxNDQ0NDc4NDAwfQ.bZo1DzrzZBetB9IP7fVip5XA_GiFBb_z8zDNTalReuU" "http://localhost:9001/products/1" | ||
``` | ||
|
||
|
||
|
||
### Press Testing | ||
see docs/press_testing.md | ||
|
||
|
||
### JWT Secret Key | ||
change `hmacSecret` var to your own. | ||
|
||
|
||
### Start the web server: | ||
|
||
revel run deer-api | ||
|
||
|
||
|
||
### Code Layout | ||
|
||
The directory structure of a generated Revel application: | ||
|
||
conf/ Configuration directory | ||
app.conf Main app configuration file | ||
routes Routes definition file | ||
|
||
app/ App sources | ||
init.go Interceptor registration | ||
controllers/ App controllers go here | ||
views/ Templates directory | ||
|
||
messages/ Message files | ||
|
||
public/ Public static assets | ||
css/ CSS files | ||
js/ Javascript files | ||
images/ Image files | ||
|
||
tests/ Test suites | ||
|
||
|
||
|
||
|
||
### Help | ||
|
||
* The [Getting Started with Revel](http://revel.github.io/tutorial/gettingstarted.html). | ||
* The [Revel guides](http://revel.github.io/manual/index.html). | ||
* The [Revel sample apps](http://revel.github.io/examples/index.html). | ||
* The [API documentation](https://godoc.org/github.com/revel/revel). |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package controllers | ||
|
||
import ( | ||
"github.com/revel/revel" | ||
) | ||
|
||
type App struct { | ||
GorpController | ||
} | ||
|
||
func (c App) Index() revel.Result { | ||
greeting := "Aloha World" | ||
return c.Render(greeting) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package controllers | ||
|
||
import ( | ||
"errors" | ||
"log" | ||
) | ||
|
||
var ( | ||
errAuthHeaderNotFound = errors.New("authorization header not found") | ||
errInvalidTokenFormat = errors.New("token format is invalid") | ||
) | ||
|
||
func checkErr(err error, msg string) { | ||
if err != nil { | ||
log.Println(msg) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package controllers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package controllers | ||
|
||
import ( | ||
"database/sql" | ||
|
||
"github.com/netqyq/deer-api/app/models" | ||
|
||
"github.com/go-gorp/gorp" | ||
_ "github.com/mattn/go-sqlite3" | ||
|
||
"github.com/revel/modules/db/app" | ||
r "github.com/revel/revel" | ||
) | ||
|
||
var ( | ||
Dbm *gorp.DbMap | ||
) | ||
|
||
type GorpController struct { | ||
*r.Controller | ||
Txn *gorp.Transaction | ||
} | ||
|
||
func InitDB1() { | ||
db.Init() | ||
Dbm = &gorp.DbMap{Db: db.Db, Dialect: gorp.SqliteDialect{}} | ||
|
||
setColumnSizes := func(t *gorp.TableMap, colSizes map[string]int) { | ||
for col, size := range colSizes { | ||
t.ColMap(col).MaxSize = size | ||
} | ||
} | ||
|
||
t := Dbm.AddTable(models.User{}).SetKeys(true, "UserId") | ||
t.ColMap("Password").Transient = true | ||
setColumnSizes(t, map[string]int{ | ||
"Email": 60, | ||
"Name": 100, | ||
}) | ||
|
||
t1 := Dbm.AddTable(models.Product{}).SetKeys(true, "Id") | ||
setColumnSizes(t1, map[string]int{ | ||
"Name": 200, | ||
}) | ||
|
||
err := Dbm.CreateTablesIfNotExists() | ||
checkErr(err, "Create tables failed") | ||
|
||
Dbm.TraceOn("[gorp]", r.INFO) | ||
Dbm.CreateTables() | ||
|
||
} | ||
|
||
func (c *GorpController) Begin() r.Result { | ||
txn, err := Dbm.Begin() | ||
if err != nil { | ||
panic(err) | ||
} | ||
c.Txn = txn | ||
return nil | ||
} | ||
|
||
func (c *GorpController) Commit() r.Result { | ||
if c.Txn == nil { | ||
return nil | ||
} | ||
if err := c.Txn.Commit(); err != nil && err != sql.ErrTxDone { | ||
panic(err) | ||
} | ||
c.Txn = nil | ||
return nil | ||
} | ||
|
||
func (c *GorpController) Rollback() r.Result { | ||
if c.Txn == nil { | ||
return nil | ||
} | ||
if err := c.Txn.Rollback(); err != nil && err != sql.ErrTxDone { | ||
panic(err) | ||
} | ||
c.Txn = nil | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package controllers | ||
|
||
import ( | ||
"log" | ||
"net/http" | ||
"strings" | ||
|
||
jwt "github.com/dgrijalva/jwt-go" | ||
"github.com/revel/revel" | ||
) | ||
|
||
func AddLog(c *revel.Controller) revel.Result { | ||
log.Println("InterceptFunc Test.") | ||
return nil | ||
} | ||
|
||
// Authenticate is and method will be called before any authenticate needed action. | ||
// In order to valid the user. | ||
func Authenticate(c *revel.Controller) revel.Result { | ||
log.Println("Authenticate!") | ||
log.Println(c.Params) | ||
|
||
tokenString, err := getTokenString(c) | ||
if err != nil { | ||
log.Println("get token string failed") | ||
c.Response.Status = http.StatusBadRequest | ||
return c.RenderJSON("get token string failed") | ||
} | ||
|
||
var claims jwt.MapClaims | ||
claims, err = decodeToken(tokenString) | ||
if err != nil { | ||
c.Response.Status = http.StatusUnauthorized | ||
return c.RenderJSON("auth failed") | ||
} | ||
log.Println("claims decode:", claims) | ||
log.Println(claims["email"]) | ||
email, found := claims["email"] | ||
if !found { | ||
log.Println(err) | ||
c.Response.Status = http.StatusBadRequest | ||
return c.RenderJSON("email not found in db") | ||
} | ||
log.Println("email found:", email) | ||
_, err = getUser(email.(string)) | ||
if err != nil { | ||
log.Println(err) | ||
c.Response.Status = http.StatusUnauthorized | ||
return c.RenderJSON("auth failed") | ||
} | ||
log.Println("auth token success") | ||
return nil | ||
} | ||
|
||
func getTokenString(c *revel.Controller) (tokenString string, err error) { | ||
authHeader := c.Request.Header.Get("Authorization") | ||
if authHeader == "" { | ||
log.Println(errAuthHeaderNotFound) | ||
return "", errAuthHeaderNotFound | ||
} | ||
|
||
tokenSlice := strings.Split(authHeader, " ") | ||
if len(tokenSlice) != 2 { | ||
return "", errInvalidTokenFormat | ||
} | ||
tokenString = tokenSlice[1] | ||
return tokenString, nil | ||
|
||
} | ||
|
||
func init() { | ||
revel.OnAppStart(InitDB1) | ||
// test | ||
revel.InterceptFunc(AddLog, revel.BEFORE, &App{}) | ||
|
||
revel.InterceptMethod((*GorpController).Begin, revel.BEFORE) | ||
revel.InterceptFunc(Authenticate, revel.BEFORE, &App{}) | ||
revel.InterceptMethod((*GorpController).Commit, revel.AFTER) | ||
revel.InterceptMethod((*GorpController).Rollback, revel.FINALLY) | ||
} |
Oops, something went wrong.