若依的自带的登录功能是成熟的,但是如果要接入上级应用的统一认证(单点登录)的话,还是需要改造一下,改造的主要目的是取消验证码可以自动登录,实现的思想网上有很多都一样,本文讲一下实践(可以测试通过的那种),最终浏览器输入GET请求就可以实现.
在
/src/view/
下新增login_sso.vue文件作为自动登录的前端入口.
如果你参考别的示例,可能第一步就卡住了,因为你的若依版本很有可能在/src/view/
目录下不支持驼峰命名.vue
文件,所以很有可能导致第二步的时候注册不了组件.
<template>
<div>div>
template>
<script>
import {getInfo} from "@/api/login";
export default {
name: "LoginSso",
data() {
return {
loginRules: {},
loading: false,
//验证码开关
captchaOnOff: true,
//注册开关
register: false,
//重定向
redirect: undefined
};
},
watch: {
},
created() {
//页面初始化时调单点登录方法
this.loginSso();
},
methods: {
loginSso(){
//获取地址栏中的code
const code = this.$route.query.code;
console.log("code="+code)
//调用登录的接口
if(code==''||code==undefined||code==null){
//请求中不带code,拦截为正常登录
}else{
this.loading = true;//开启过渡动画
const loginInfo = {
"code" : code
};
//执行另一套登录操作
//不是本系统的用户,去J平台登陆去
this.$store.dispatch("LoginSso", loginInfo).then(() => {
this.$message.success("登录成功");
this.loading = false;
//判断当前角色
getInfo().then((res) => {
//获取角色名称
var rolesName = res.roles[0];
//获取所属场馆
this.deptInfo = res.dept;
sessionStorage.setItem("ssUserName", res.user.nickName);
//处理登录
this.$router.push({path: this.redirect || "/"}).catch(() => {});
});
}).catch(err=> {
console.log("有异常信息",err);
this.loading = false;
});
}
},
}
};
script>
<style rel="stylesheet/scss" lang="scss">
style>
// 公共路由
export const constantRoutes = [
//...
{
path: '/login',
component: () => import('@/views/login'),
hidden: true
},
//注册单点登录页面
{
path: '/loginSso',
component: () => import('@/views/login_sso'),
hidden: true
},
//...
//增加单点登录白名单
const whiteList = ['/login', '/auth-redirect', '/bind', '/register','/loginSso']
后端将提供无验证码的登录接口
//单点登录 无验证码登录
export function loginSso(queryParam) {
return request({
url: '/loginSso',
method: 'post',
params: queryParam
})
}
切记注意一下文件顶部的
import
,有可能引入的组件内的其他方法没有引入会导致方法undefined
.
import {login, logout, getInfo, loginSso} from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
//...
actions: {
//...
//单点登录
LoginSso({commit}, userInfo){
const code = userInfo.code;
const queryParams = {
'code' : code
};
return new Promise((resolve, reject) => {
loginSso(queryParams).then(res => {
setToken(res.token)
commit('SET_TOKEN', res.token)
resolve()
}).catch(error => {
reject(error)
})
})
}
//...
}
<dependency>
<groupId>org.joddgroupId>
<artifactId>jodd-httpartifactId>
<version>6.3.0version>
dependency>
package com.ruoyi.web.controller.system;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.system.service.ISysUserService;
import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
/**
* 单点登录Controller
* @author sgc
*/
@Slf4j
@RestController
public class LoginSsoController {
@Autowired
private ISysUserService userService;
@Autowired
private SysLoginService loginService;
@PostMapping("/loginSso")
public AjaxResult loginSso(String token) {
//这里进行单点登录上级系统的令牌校验 写自己的逻辑
JSONObject ssoObject = this.checkSsoToken(token);
//处理结果 {code=xxx,data={xxx},msg=xxx}
String code = ssoObject.getString("code");
String loginName = null;
AjaxResult ajax = null;
if (code.equals("0")) {//验证成功需要自动登录
JSONObject dataObject = ssoObject.getJSONObject("data");
//拿到登录名
loginName = dataObject.getString("userName");
} else {//验证失败返回失败信息
ajax = AjaxResult.error(ssoObject.getString("msg"));
return ajax;
}
//组装checkUserNameUnique方法需要的SysUser对象,检测用户名存在否
SysUser loginUser = new SysUser();
loginUser.setUserName(loginName);
if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(loginUser))) {
log.info("单点登录用户[{}]已存在.", loginName);
} else {
log.info("单点登录用户[{}]不存在, 需要创建.", loginName);
//将上级系统带过来的用户创建个新用户存储起来
SysUser sysUser = this.createSsoUser(ssoObject);
userService.insertUser(sysUser);
}
//生成本系统的令牌给到前端进行登录
ajax = AjaxResult.success();
//这里设置单点登录用户默认密码为123456
String tokenNew = loginService.loginNoCaptcha(loginName, "123456", null);
ajax.put("token", tokenNew);
ajax.put("msg", "登录成功");
return ajax;
}
//处理父级系统传过来的令牌进行校验返回登录信息结果
JSONObject checkSsoToken(String token) {
JSONObject jsonObject = new JSONObject();
//测试代码
jsonObject.put("code","0");
jsonObject.put("msg","验证成功");
JSONObject jsonObjectData =new JSONObject();
jsonObjectData.put("userName","sso1");
jsonObjectData.put("nickName","单点1");
jsonObject.put("data",jsonObjectData);
//测试环境
String baseUrl = "http://xxxxx/xxx/check?code=" + token;//根据实际地址进行修改
//...
//进行逻辑校验...
//...
return jsonObject;
}
//组装单点登录的用户对象 将来存入本系统
SysUser createSsoUser(JSONObject ssoObject){
JSONObject dataObject = ssoObject.getJSONObject("data");
//从父级系统拿到的用户信息
String userId = dataObject.getString("userId");
String companyId = dataObject.getString("companyId");
String companyName = dataObject.getString("companyName");
String deptId = dataObject.getString("deptId");
String userName = dataObject.getString("userName");
String nickName = dataObject.getString("nickName");
String email = dataObject.getString("email");
String phonenumber = dataObject.getString("phonenumber");
String sex = dataObject.getString("sex");
String avatar = dataObject.getString("avatar");
//组装本系统用户信息
SysUser sysUser = new SysUser();
sysUser.setUserName(userName);
sysUser.setNickName(nickName);
sysUser.setPassword(SecurityUtils.encryptPassword("123456"));
sysUser.setCreateBy("sso");
sysUser.setCreateTime(new Date());
sysUser.setDeptId(202L);//所属部门 建议在后管新增一个部门进行初始化
Long[] roleIds = {100L};
sysUser.setRoleIds(roleIds);//归属角色 建议在后管新增一个角色进行初始化
return sysUser;
}
}
在
com.ruoyi.framework.web.service.SysLoginService
中增加一个无验证码登录的方法loginNoCaptcha
.
/**
* 无需验证码登录
* 重写login方法将验证码模块去掉
* @param username
* @param password
* @param uuid
* @return
*/
public String loginNoCaptcha(String username, String password, String uuid)
{
// 用户验证
Authentication authentication = null;
try
{
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager.authenticate(authenticationToken);
}
catch (Exception e)
{
if (e instanceof BadCredentialsException)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
else
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUserId());
// 生成token
return tokenService.createToken(loginUser);
}
增加后端对于
/loginSso
的放行.
//...
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
.antMatchers("/login", "/register", "/captchaImage","/loginSso").permitAll()
//...