目录
1、业务场景
若依的自带的登录功能是成熟的,但是如果要接入上级应用的统一认证(单点登录)的话,还是需要改造一下,改造的主要目的是取消验证码可以自动登录,实现的思想网上有很多都一样,本文讲一下实践(可以测试通过的那种),最终浏览器输入GET请求就可以实现.
2、前端改造
2.1、新增login_sso.vue
在
/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>
2.2、index.js注册路由
// 公共路由 export const constantRoutes = [ //... { path: '/login', component: () => import('@/views/login'), hidden: true }, //注册单点登录页面 { path: '/loginSso', component: () => import('@/views/login_sso'), hidden: true }, //...
2.3、permission.js增加白名单过滤
//增加单点登录白名单 const whiteList = ['/login', '/auth-redirect', '/bind', '/register','/loginSso']
2.4、login.js增加后端登录接口
后端将提供无验证码的登录接口
//单点登录 无验证码登录 export function loginSso(queryParam) { return request({ url: '/loginSso', method: 'post', params: queryParam }) }
2.5、user.js增加单点登录方法
切记注意一下文件顶部的
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) }) }) } //... }
3、后端改造
3.1、引入依赖
<!-- https://mvnrepository.com/artifact/org.jodd/jodd-http --> <dependency> <groupId>org.jodd</groupId> <artifactId>jodd-http</artifactId> <version>6.3.0</version> </dependency>
3.2、新增LoginSsoController单点登录控制器
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; } }
3.3、增加单点登录后端方法
在
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); }
3.4、增加shiro白名单
增加后端对于
/loginSso
的放行.
//... // 对于登录login 注册register 验证码captchaImage 允许匿名访问 .antMatchers("/login", "/register", "/captchaImage","/loginSso").permitAll() //...
# 📝结尾
看到这里了还不给博主扣个:- ⛳️ 点赞☀️收藏 ⭐️ 关注!- 💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖- 拜托拜托这个真的很重要!- 你们的点赞就是博主更新最大的动力!- 有问题可以评论或者私信呢秒回哦。