一、 redis 的链接
在myapp 里面创建 redis 文件夹,文件夹里面创建一个redis_manage.go 文件。redis 使用的是garyburd 的rdigo 模块。redis 安装时,需要配置运行远程链接才行,具体步骤可以查看网上教程。
package redismanage
import (
"fmt"
redigo "github.com/garyburd/redigo/redis"
)
type redismanage struct {}
type Conn = redigo.Conn; // 透传redigo.Conn 类型定义
type Pool = *redigo.Pool;
var RedisString = redigo.String;
var pool Pool;
//创建redismanage实例的函数
func NewRedisManage () *redismanage{
return &redismanage{}
}
// 获取链接redis 的句柄
func (*redismanage) RedigoConnGet() Conn {
return pool.Get()
}
// 设置redis 连接池
func (*redismanage) RedigoConnInit() {
redis_host := "xx.xx.xx.xx" //redis 安装的服务器ip 或者域名
redis_port := 6379 //redis 使用的端口号
pool_size := 20 //连接池的大小
pool = redigo.NewPool(func() (redigo.Conn, error) {
c, err := redigo.Dial("tcp", fmt.Sprintf("%s:%d", redis_host, redis_port),redigo.DialPassword("password")) //redis 如果配置了密码,这里就要配置对应的密码
if err != nil {
return nil, err
}
return c, nil
}, pool_size)
}
二、 gojwt 模块的创建
在myapp 里面创建一个gojwt 文件夹,文件夹里面创建一个gojwt.go的文件
package gojwt
import (
"github.com/dgrijalva/jwt-go"
jwtmiddleware "github.com/iris-contrib/middleware/jwt"
)
//参考的链接网站 http://studyiris.com/example/exper/jwt.html
type gojwt struct{}
type StandardClaims = jwt.StandardClaims;
type JwtToken = jwt.Token;
func NewGoJwt() *gojwt{
return &gojwt{}
}
//加密密钥
var JwtSecret=[]byte("secret")
//Claim是一些实体(通常指的用户)的状态和额外的元数据
type Claims struct{
Username string `json:"username"`
Password string `json:"password"`
jwt.StandardClaims
}
//RefreshClaims
type RefreshClaims struct{
Token string ``
jwt.StandardClaims
}
// 生成token
func (*gojwt) GenerateToken(secret []byte, claims Claims) (tokenString string, err error) {
// 创建一个新的令牌对象,指定签名方法和声明
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// 使用密码签名 并获得完整的编码令牌作为字符串
tokenString, err = token.SignedString(secret)
return tokenString, err
}
// 生成refreshoken
func (*gojwt) GenerateRefreshToken(secret []byte, refreshClaims RefreshClaims) (refreshTokenString string, err error) {
// 创建一个新的令牌对象,指定签名方法和声明
token := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims)
// 使用密码签名并获得完整的编码令牌作为字符串
refreshTokenString, err = token.SignedString(secret)
return refreshTokenString ,err
}
func (*gojwt) CreateJwtMiddleware() *jwtmiddleware.Middleware {
jwtHandler := jwtmiddleware.New(jwtmiddleware.Config{
//这个方法将验证jwt的token
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
//自己加密的秘钥或者说盐值
return JwtSecret, nil
},
//设置后,中间件会验证令牌是否使用特定的签名算法进行签名
//如果签名方法不是常量,则可以使用ValidationKeyGetter回调来实现其他检查
//重要的是要避免此处的安全问题:https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
//加密的方式
SigningMethod: jwt.SigningMethodHS256,
//验证未通过错误处理方式
//ErrorHandler: func(context.Context, string)
//debug 模式
//Debug: bool
});
return jwtHandler
}
三、 创建login 接口
login接口需要实现,账号登录成功后,生成token 和refresh token,写入redis 和mysql 里面,同时设置redis 的有效时限。最后把token 返回给前端。
package main
import (
"fmt"
gojwt "main/gojwt"
mysqlmanage "main/mysql"
redismanage "main/redis"
"strings"
"time"
"github.com/garyburd/redigo/redis"
"github.com/kataras/iris/v12"
)
var MysqlManage = mysqlmanage.NewMysqlManage();
var RedisManage = redismanage.NewRedisManage();
var Gojwt = gojwt.NewGoJwt();
// 设置redis 连接池
func init() {
RedisManage.RedigoConnInit();
MysqlManage.MysqlmanageInit();
}
func main() {
//链接redis
defer RedisManage.RedigoConnGet().Close();
defer MysqlManage.MysqlConnGet().Close();
app := iris.New()
app.OnErrorCode(iris.StatusNotFound, notFound)
app.Post("/login", loginHandle)
app.Run(iris.Addr(":8080"),iris.WithCharset("UTF-8"))
}
func notFound(ctx iris.Context){
code := ctx.GetStatusCode()
msg := "not found"
ctx.JSON(iris.Map{
"Message": msg,
"Code": code,
})
}
func index(ctx iris.Context) {
fmt.Println("compeleted")
ctx.JSON(iris.Map{
"Message": "ok",
});
}
func status(ctx iris.Context) {
ctx.JSON(iris.Map{"Message": "OK"})
}
func loginHandle (ctx iris.Context){
var name = ctx.PostValue("name");
var password = ctx.PostValue("password");
fmt.Printf("%s %s",name,password);
var users []mysqlmanage.User;
MysqlManage.MysqlConnGet().Where(&mysqlmanage.User{Name: name, Password: password}).First(&users)
if len( users) == 0 {
ctx.JSON(iris.Map{
"Message": "账号或者密码不正确!"})
return
}
claims := gojwt.Claims{
Username:name,
Password:password,
StandardClaims: gojwt.StandardClaims{
IssuedAt:time.Now().Unix(), // 过期时间,必须设置
Issuer:"wang", // 可不必设置,也可以填充用户名,
},
}
if token,err :=Gojwt.GenerateToken(gojwt.JwtSecret, claims); err == nil {
refreshClaims := gojwt.RefreshClaims{
Token:token,
StandardClaims:gojwt.StandardClaims{
ExpiresAt:time.Now().Add(72*time.Hour).Unix(), // 过期时间,必须设置
Issuer:"wang", // 可不必设置,也可以填充用户名,
},
}
refreshToken,_ := Gojwt.GenerateRefreshToken(gojwt.JwtSecret,refreshClaims)
tokenString := strings.Split(token,".")[2]
refreshTokenString:= strings.Split(refreshToken,".")[2]
//往redis 里面填写token 和 refreshToken
conn:= RedisManage.RedigoConnGet();
conn.Do("SET", tokenString, token,"EX", 60*5)
conn.Do("SET", refreshTokenString,refreshToken,"EX",60*60*24*7)
MysqlManage.MysqlmanageAlterRecord(name,password,tokenString,refreshTokenString);
ctx.JSON(iris.Map{
"data":iris.Map{
"token":tokenString,
"refreshToken":refreshTokenString},
"Message": "OK",
"responseCode":1000000,
})
} else {
fmt.Printf("%s",token)
ctx.JSON(iris.Map{
"Message": "error"})
}
}
成功运行后的结果:
四、后续调用的接口的token 验证
func main() {
//链接redis
defer RedisManage.RedigoConnGet().Close();
defer MysqlManage.MysqlConnGet().Close();
app := iris.New()
app.OnErrorCode(iris.StatusNotFound, notFound)
app.Get("/",myHandler,index)
app.Run(iris.Addr(":8080"),iris.WithCharset("UTF-8"))
}
func index(ctx iris.Context) {
fmt.Println("compeleted")
ctx.JSON(iris.Map{
"Message": "ok",
});
}
//获取request header 里面的 Authorization 字段
func FromAuthHeader(ctx iris.Context) (string, error) {
authHeader := ctx.GetHeader("Authorization")
if authHeader == "" {
return "", nil // No error, just no token
}
authHeaderParts := strings.Split(authHeader, " ")
if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != "bearer" {
return "", fmt.Errorf("Authorization header format must be Bearer {token}")
}
return authHeaderParts[1], nil
}
// 中间件写法,验证token是否有效
func myHandler(ctx iris.Context) {
token,_ :=FromAuthHeader(ctx)
conn:= RedisManage.RedigoConnGet();
_, err := redis.String(conn.Do("GET", token))
if err != nil {
fmt.Println("redis get failed:", err)
ctx.JSON(iris.Map{
"Message": "token is invalid"})
} else {
conn.Do("SET", tokenString, token,"EX", 60*5) //重新刷新token 的时效
ctx.Next();
}
}
五、refreshToken 接口
func refreshTokenHandle(ctx iris.Context){
var refreshToken = ctx.PostValue("refreshToken");
conn:= RedisManage.RedigoConnGet();
_, err := redis.String(conn.Do("GET", refreshToken))
if err != nil {
ctx.JSON(iris.Map{
"Message": "refreshToken is invalid"})
} else {
mysqlconn := MysqlManage.MysqlConnGet();
var users []mysqlmanage.User;
mysqlconn.Where("refreshToken = ?", refreshToken).First(&users)
if len(users)>0 {
token:= users[0].Token;
tokenString:= strings.Split(token,".")[2];
conn:= RedisManage.RedigoConnGet();
conn.Do("SET", tokenString, token,"EX", 60*1);
ctx.JSON(iris.Map{
"Message": "token is updated"})
}else {
ctx.JSON(iris.Map{
"Message": "refreshToken is invalid"})
}
}
}