一步步实现微服务权限管理系统(5)
前言
- 前端参考了很多框架,可谓百花齐放,但很多封装过剩,不利于学习和应用,最终我选择了 [vue-pure-admin](https://github.com/pure-admin/vue-pure-admin)
- 后端我将使用 go-zero 来带领大家一步步实现一个权限管理系统
- 本系列项目存放在 purezeroadmin 中,每一部分我都将打tag,并保证每个tag能正常运行。请多点赞和评论。
- 后面示例均为
purezeroadmin
项目为例,你们可以根据需要自建工程来进行试验。均采用vscode
进行试验。 go-zero
常用命令我将放入其对应的makefile
文件中。
待解决
- 当前后端只修改了一个
userloginhandler.go
,还有其他handler都需要修改,如果以后增加业务都要做相关的修改,那太麻烦了。 base-api/base.api
不再需要了- 前端修改对应格式
后端自定义 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.go
和 userrouterhandler.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.go
和userrouterlogic.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认证