若依ruoyi框架实现单点登录或者接入统一认证

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()
//...

猜你喜欢

转载自blog.csdn.net/csdn_avatar_2019/article/details/129306927