Skip to content

一步步实现微服务权限管理系统(5)

前言

  • 前端参考了很多框架,可谓百花齐放,但很多封装过剩,不利于学习和应用,最终我选择了 [vue-pure-admin](https://github.com/pure-admin/vue-pure-admin)
  • 后端我将使用 go-zero 来带领大家一步步实现一个权限管理系统
  • 本系列项目存放在 purezeroadmin 中,每一部分我都将打tag,并保证每个tag能正常运行。请多点赞和评论。
  • 后面示例均为 purezeroadmin 项目为例,你们可以根据需要自建工程来进行试验。均采用vscode进行试验。
  • go-zero 常用命令我将放入其对应的 makefile 文件中。

待解决

  1. 当前后端只修改了一个 userloginhandler.go,还有其他handler都需要修改,如果以后增加业务都要做相关的修改,那太麻烦了。
  2. base-api/base.api 不再需要了
  3. 前端修改对应格式

后端自定义 handler 模板

初始化模板

back-end操作

 goctl template init
Templates are generated in /root/.goctl/1.7.3, edit on your risk!
# 拷贝到该项目,保证项目与项目模板依赖独立 cp ~/.goctl/1.7.3/ template

修改handler 模板

vim template/api/handler.tpl

package {{.PkgName}}

import (
    "net/http"

    "github.com/zeromicro/go-zero/rest/httpx"
    {{.ImportPackages}}

    xhttp "github.com/zeromicro/x/http"
)

{{if .HasDoc}}{{.Doc}}{{end}}
func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        {{if .HasRequest}}var req types.{{.RequestType}}
        if err := httpx.Parse(r, &req); err != nil {
            xhttp.JsonBaseResponseCtx(r.Context(), w, err)
            return
        }

        {{end}}l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx)
        {{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}})
        if err != nil {
            xhttp.JsonBaseResponseCtx(r.Context(), w, err)
        } else {
            {{if .HasResp}}xhttp.JsonBaseResponseCtx(r.Context(), w, resp){{else}}xhttp.JsonBaseResponseCtx(r.Context(), w, nil){{end}}
        }
    }
}

生成新handler

userrefreshtokenhandler.gouserrouterhandler.go 需要重新生成,先删除对应的文件,再执行命令

goctl api  go --api user-api/api/user.api --dir user-api --home template

删除base-api依赖

  • 删除base-api/base.api

  • user-api/api/user.api修改

type (
    UserLoginReq {
        UserName string `json:"username"`
        Password string `json:"password"`
    }
    UserLoginResp {
        Avatar       string   `json:"avatar"`
        Username     string   `json:"username"`
        Nickname     string   `json:"nickname"`
        Roles        []string `json:"roles"`
        Permissions  []string `json:"permissions"`
        AccessToken  string   `json:"accessToken"`
        RefreshToken string   `json:"refreshToken"`
        Expires      string   `json:"expires"`
    }
)

service user-api {
    @doc "用户登录"
    @handler userLogin
    post /api/login (UserLoginReq) returns (UserLoginResp)
}

type (
    UserRefreshTokenReq {
        RefreshToken string `json:"refreshToken"`
    }
    UserRefreshTokenResp {
        AccessToken  string `json:"accessToken"`
        RefreshToken string `json:"refreshToken"`
        Expires      string `json:"expires"`
    }
)

service user-api {
    @doc "刷新token"
    @handler UserRefreshToken
    post /api/refresh-token (UserRefreshTokenReq) returns (UserRefreshTokenResp)
}

type RouterData {
    Path      string       `json:"path"`
    Name      string       `json:"name,omitempty"`
    Component string       `json:"component,omitemty"`
    Meta      Meta         `json:"meta"`
    Children  []RouterData `json:"children,omitempty"`
}

type Meta {
    Title string   `json:"title"`
    Icon  string   `json:"icon,omitempty"`
    Rank  int64    `json:"rank,omitempty"`
    Roles []string `json:"roles,omitempty"`
    Auths []string `json:"auths,omitempty"`
}

type (
    UserRouterReq  {}
)

service user-api {
    @doc "获取路由"
    @handler userRouter
    get /api/get-async-routes (UserRouterReq) returns ([]RouterData)
}
  • 修改 userrefreshtokenlogic.gouserrouterlogic.go
// user-api/internal/logic/userrefreshtokenlogic.go
func (l *UserRefreshTokenLogic) UserRefreshToken(req *types.UserRefreshTokenReq) (resp *types.UserRefreshTokenResp, err error) {
    if req.RefreshToken != "" {
        return &types.UserRefreshTokenResp{
            AccessToken:  "eyJhbGciOiJIUzUxMiJ9.newAdmin",
            RefreshToken: "eyJhbGciOiJIUzUxMiJ9.newAdminRefresh",
            Expires:      "2030/10/30 23:59:59",
        }, nil
    }
    return nil, errors.New("refresh token error")
}

// user-api/internal/logic/userrouterlogic.go
func (l *UserRouterLogic) UserRouter(req *types.UserRouterReq) (resp []*types.RouterData, err error) {
    return []*types.RouterData{
        {
            Path: "/permission/page/index",
            Name: "PermissionPage",
            Meta: types.Meta{
                Title: "页面权限",
                Roles: []string{"admin", "common"},
            },
        },
        {
            Path: "/permission/button",
            Meta: types.Meta{
                Title: "按钮权限",
                Roles: []string{"admin", "common"},
            },
            Children: []types.RouterData{
                {
                    Path:      "/permission/button/router",
                    Component: "permission/button/index",
                    Name:      "PermissionButtonRouter",
                    Meta: types.Meta{
                        Title: "路由返回按钮权限",
                        Auths: []string{
                            "permission:btn:add",
                            "permission:btn:edit",
                            "permission:btn:delete",
                        },
                    },
                },
                {
                    Path:      "/permission/button/login",
                    Component: "permission/button/perms",
                    Name:      "PermissionButtonLogin",
                    Meta: types.Meta{
                        Title: "登录返回按钮权限",
                    },
                },
            },
        },
    }, nil
}

测试

 curl --request POST \
  --url http://localhost:8888/api/login\?debug \
  --header 'Content-Type: application/json' \
  --data '{
    "username": "admin",
    "password": "admin123"
}'
{"code":0,"msg":"ok","data":{"avatar":"https://avatars.githubusercontent.com/u/44761321","username":"admin","nickname":"小铭","roles":["admin"],"permissions":["*:*:*"],"accessToken":"eyJhbGciOiJIUzUxMiJ9.admin","refreshToken":"eyJhbGciOiJIUzUxMiJ9.adminRefresh","expires":"2030/10/30 00:00:00"}}# curl --request POST \
  --url http://localhost:8888/api/refresh-token \
  --header 'Content-Type: application/json' \
  --data '{
    "refreshToken": "xx"
}'
{"code":0,"msg":"ok","data":{"accessToken":"eyJhbGciOiJIUzUxMiJ9.newAdmin","refreshToken":"eyJhbGciOiJIUzUxMiJ9.newAdminRefresh","expires":"2030/10/30 23:59:59"}}# curl http://localhost:8888/api/get-async-routes
{"code":0,"msg":"ok","data":[{"path":"/permission/page/index","name":"PermissionPage","component":"","meta":{"title":"页面权限","roles":["admin","common"]}},{"path":"/permission/button","component":"","meta":{"title":"按钮权限","roles":["admin","common"]},"children":[{"path":"/permission/button/router","name":"PermissionButtonRouter","component":"permission/button/index","meta":{"title":"路由返回按钮权限","auths":["permission:btn:add","permission:btn:edit","permission:btn:delete"]}},{"path":"/permission/button/login","name":"PermissionButtonLogin","component":"permission/button/perms","meta":{"title":"登录返回按钮权限"}}]}]}# 

前端修改对应接口

  • src/api/routes.ts
type Result = {
  code: number;
  msg: string;
  data: Array<any>;
};
  • src/api/user.ts
export type UserResult = {
  code: number;
  msg: string;
  data: {
    /** 头像 */
    avatar: string;
    /** 用户名 */
    username: string;
    /** 昵称 */
    nickname: string;
    /** 当前登录用户的角色 */
    roles: Array<string>;
    /** 按钮级别权限 */
    permissions: Array<string>;
    /** `token` */
    accessToken: string;
    /** 用于调用刷新`accessToken`的接口时所需的`token` */
    refreshToken: string;
    /** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
    expires: Date;
  };
};

export type RefreshTokenResult = {
  code: number;
  msg: string;
  data: {
    /** `token` */
    accessToken: string;
    /** 用于调用刷新`accessToken`的接口时所需的`token` */
    refreshToken: string;
    /** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
    expires: Date;
  };
};
  • src/store/modules/user.ts
/** 登入 */
async loginByUsername(data) {
    return new Promise<UserResult>((resolve, reject) => {
    getLogin(data)
        .then(data => {
        if (data?.code === 0) setToken(data.data);
        resolve(data);
        })
        .catch(error => {
        reject(error);
        });
    });
},
  • src/views/login/index.vue
const onLogin = async (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  await formEl.validate((valid, fields) => {
    if (valid) {
      loading.value = true;
      useUserStoreHook()
        .loginByUsername({ username: ruleForm.username, password: "admin123" })
        .then(res => {
          if (res.code === 0) {
            // 获取后端路由
            return initRouter().then(() => {
              router.push(getTopMenu(true).path).then(() => {
                message("登录成功", { type: "success" });
              });
            });
          } else {
            message("登录失败", { type: "error" });
          }
        })
        .finally(() => (loading.value = false));
    }
  });
};
  • src/views/permission/page/index.vue
function onChange() {
  useUserStoreHook()
    .loginByUsername({ username: username.value, password: "admin123" })
    .then(res => {
      if (res.code) {
        storageLocal().removeItem("async-routes");
        usePermissionStoreHook().clearAllCachePage();
        initRouter();
      }
    });
}

测试

以上修改完毕,运行前后端测试通过

tag版本

purezeroadmin 项目下

git checkout v1.4.0

接下来

jwt认证

Comments