企业级单点登录实现方案OAuth2——前端登录及请求实现参考
当后端使用Spring Security Oauth2的方式控制权限时,密码模式下前端有两个地方的实现需要关注,一是登录;二是登录后的其它接口调用。
本示例为Vue实现,使用axios来进行接口调用。
1. 登录
登录需要需要调用/oauth/token接口,并传入相关登录信息,代码实现如下:
// 组装登录参数
let params = {
username: this.userLoginForm.username,
password: this.passwordEncode(this.userLoginForm.password),
id: "testResource",
client_id: "test",
client_secret: "secret",
scope: "read",
grant_type: "password",
type: "user",
};
// 发起请求
this.$postUrlParams("/oauth/token", params)
.then((res) => {
this.loading = false;
this.$cacheToken(res.access_token);
// 登录成功后获取用户信息
...
})
.catch(() => {
this.loading = false;
});
注意
1. 登录成功后,我们需要将返回的token存储起来,以便在后续请求中使用这个Token。(如何使用后文会说明,可以将这个token存储到localStorage中)
2. 相关参数一定要与后端资源服务器中配置的信息匹配,否则会登录失败;
3. 客户端模式与上述过程类似,但传的参数不一样,不需要用户名及密码;
其中postUrlPrarams方法定义如下:
const myAxios = axios.create({
baseURL: "/api/front",
timeout: 20000,
headers: {
post: {
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
},
},
});
function postUrlParams(url, param) {
return new Promise((resolve, reject) => {
myAxios
.post(url, param)
.then(
(response) => {
resolve(response);
},
(err) => {
if (err.status === 404) {
Message.error("系统异常(404)");
} else {
reject(err);
}
}
)
.catch((error) => {
console.log(error);
reject(error);
});
});
}
实现后登录接口请求内容如下所示:
登录请求内容
注意content-type需要是指定类型的;请求的数据需要是“表单数据”这种格式,其它格式都会报错。
2. 登录后接口调用
登录后调用后端接口时,需要在请求头中添加Authentication项,设置其值为bearer + 空格 + 登录返回的token;使用axios时,可以通过interceptor来统一处理:
const myAxios = axios.create({
baseURL: "/api/front",
timeout: 20000,
headers: {
post: {
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
},
},
});
//POST传参序列化(添加请求拦截器)
myAxios.interceptors.request.use(
(config) => {
if (config.method === "post") {
let contentType = config.headers["Content-Type"];
if (contentType !== "application/json;charset=UTF-8" && contentType !== "multipart/form-data") {
config.data = qs.stringify(config.data);
}
}
let token = window.localStorage.getItem("ttAccessToken");
// 登录请求中不能包含有token,否则会有报异常
// 其它请求将token带上
if (token && config.url.indexOf("/oauth/token") === -1) {
config.withCredentials = true;
config.headers.Authorization = "Bearer " + token;
}
return config;
},
(error) => {
console.log("错误的传参");
return Promise.reject(error);
}
);
同时建议处理当后端返回401、402等情况时,重新跳转到登录页面,如:
//返回状态判断(添加响应拦截器)
myAxios.interceptors.response.use(
(res) => {
if (res.data.code == "200") {
return Promise.resolve(res.data.data);
} else if (res.data.access_token) {
return Promise.resolve(res.data);
}
console.log(res.data);
Message.error(res.data.msg || "系统异常");
return Promise.reject(res.data);
},
(error) => {
console.log(error);
let resp = error.response;
let status = resp.status;
if (status === 401 || status === 402) {
window.localStorage.removeItem("ttAccessToken");
store.commit("needLogin");
} else if (status === 500 && !resp.data.error_description) {
// 如果后端返回的数据中包含code,那么不需要做通用处理,需要各个页面做特殊处理
var msg = resp.data.msg || "系统错误";
Message.error(msg);
} else if (status === 403) {
Message.error("权限不足");
} else if (status === 404) {
Message.error("系统异常(404)");
return Promise.reject("系统异常(404)");
} else {
Message.error(resp.data.error_description);
}
return Promise.reject(resp.data);
}
);
当然具体实现要根据实际业务场景及前后端交互时的接口方式,以上代码仅供参考。
请求的示例如下:
带token的请求示例