cas学习1:某J 集成 cas5.3res api登录

在学习一个开源项目是时集成了cas,但文档过于简单,研究了两天这次记录做个补充

1.下载cas项目

 GitHub - apereo/cas-overlay-template at 5.3

2.编译

解压zip,命令行进去,执行mvn clean package
结束之后会出现 target 文件夹,里面有一个cas.war包,这个cas.war包就是我们要运行的程序。


cas怎么调用cas认证服务的  cas.authn.rest.uri=http://localhost:9999/sys/casLogin

当用户点击登录后,cas会发送post请求到http://localhost:9999/sys/casLogin (注意是get请求,否则是调用不到的,也不报错,坑,这块坑了半天时间...)
并且把用户信息以"用户名:密码"进行Base64编码放在authorization请求头中。
若输入用户名密码为:admin/123456;那么请求头包括:
authorization=Basic Base64(admin+MD5(123))
那么发送后客户端必须响应一下数据,cas明确规定如下:
    ● cas 服务端会通过post请求,并且把用户信息以”用户名:密码”进行Base64编码放在authorization请求头中
    ● 200状态码:并且格式为
{“@class”:”org.apereo.cas.authentication.principal.SimplePrincipal”,”id”:”casuser”,”attributes”:{}}是成功的
    ● 403状态码:用户不可用
    ● 404状态码:账号不存在
    ● 423状态码:账户被锁定
    ● 428状态码:过期
    ● 其他登录失败

整体思路

3.配置

修改cas.war中的cas\WEB-INF\classes配置文件

cas服务端配置
pom.xml添加以下rest依赖
 

<dependency>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-server-support-rest-authentication</artifactId>
    <version>${cas.version}</version>
</dependency>


 
application.properties添加如下配置

 

#是否开启json识别功能,默认为false
cas.serviceRegistry.initFromJson=true
#忽略https安全协议,使用 HTTP 协议
cas.tgc.secure=false
cas.authn.rest.uri=http://localhost:9999/sys/casLogin

兼容http请求:

修改cas\WEB-INF\classes\services\HTTPSandIMAPS-10000001.json 加入http

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "^(https|http|imaps)://.*",
  "name" : "HTTPS and IMAPS",
  "id" : 10000001,
  "description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
  "evaluationOrder" : 10000
}


#如果密码有加密,打开下面配置,我的是明文

#cas.authn.rest.passwordEncoder.type=DEFAULT
#cas.authn.rest.passwordEncoder.characterEncoding=UTF-8
#cas.authn.rest.passwordEncoder.encodingAlgorithm=MD5
 

4.启动cas服务

把cas.war放到tomcat里启动就行

5.rest-client接口代码

实体CasUser

package org.jeecg.modules.system.entity;


import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;

import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: zfq
 * @date: 2024
 * @description: cas-rest返回cas服务端信息
 */
public class CasUser {

    @JsonProperty("id")
    @NotNull
    private String username;

    /**
     * 需要返回实现org.apereo.cas.authentication.principal.Principal的类名接口
     */
    @JsonProperty("@class")
    private String clazz = "org.apereo.cas.authentication.principal.SimplePrincipal";


    @JsonProperty("attributes")
    private Map<String, Object> attributes = new HashMap<>();

    @JsonIgnore
    @NotNull
    private String password;

    /**
     * 用户状态,根据状态判断是否可用
     */
    @JsonIgnore
    private String state;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getClazz() {
        return clazz;
    }

    public void setClazz(String clazz) {
        this.clazz = clazz;
    }

    public Map<String, Object> getAttributes() {
        return attributes;
    }

    public void setAttributes(Map<String, Object> attributes) {
        this.attributes = attributes;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    @JsonIgnore
    public CasUser addAttribute(String key, Object val) {
        getAttributes().put(key, val);
        return this;
    }
}

接口casLogin

/**
	 * 1. cas 服务端会通过post请求,并且把用户信息以"用户名:密码"进行Base64编码放在authorization请求头中
	 * 2. 返回200状态码并且格式为{"@class":"org.apereo.cas.authentication.principal.SimplePrincipal","id":"casuser","attributes":{}} 是成功的
	 * 2. 返回状态码403用户不可用;404账号不存在;423账户被锁定;428过期;其他登录失败
	 * @param httpHeaders
	 * @return
	 */
	@RequestMapping("/casLogin")
	public Object login(@RequestHeader HttpHeaders httpHeaders){
		CasUser casUser=new CasUser();
		try {
			UserTemp userTemp = obtainUserFormHeader(httpHeaders);
			//尝试查找用户库是否存在

			LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
			queryWrapper.eq(SysUser::getUsername,userTemp.username);
			SysUser user = sysUserService.getOne(queryWrapper);

			String userpassword = PasswordUtil.encrypt(user.getUsername(), userTemp.password, user.getSalt());
			//{“@class”:”org.apereo.cas.authentication.principal.SimplePrincipal”,”id”:”casuser”,”attributes”:{}}是成功的
			casUser.setUsername(user.getUsername());

			if (user != null) {
				if (!user.getPassword().equals(userpassword)) {
					//密码不匹配
					return new ResponseEntity(HttpStatus.BAD_REQUEST);
				}
				if (!"1".equals(user.getStatus())) {// 状态(1:正常  2:冻结 )
					//用户已锁定
					return new ResponseEntity(HttpStatus.LOCKED);
				}

			} else {
				//不存在 404
				return new ResponseEntity(HttpStatus.NOT_FOUND);
			}
		} catch (UnsupportedEncodingException e) {
			new ResponseEntity(HttpStatus.BAD_REQUEST);
		}
		//成功返回json
		return casUser;
	}

	/**
	 * This allows the CAS server to reach to a remote REST endpoint via a POST for verification of credentials.
	 * Credentials are passed via an Authorization header whose value is Basic XYZ where XYZ is a Base64 encoded version of the credentials.
	 * @param httpHeaders
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	private UserTemp obtainUserFormHeader(HttpHeaders httpHeaders) throws UnsupportedEncodingException {

		//cas服务端会通过把用户信息放在请求头authorization中,并且通过Basic认证方式加密
		String authorization = httpHeaders.getFirst("authorization");
		if(StringUtils.isEmpty(authorization)){
			return null;
		}

		String baseCredentials = authorization.split(" ")[1];
		//用户名:密码
		String usernamePassword = new String(Base64Utils.decodeFromString(baseCredentials), "UTF-8");
		String[] credentials = usernamePassword.split(":");

		return new UserTemp(credentials[0], credentials[1]);
	}

	/**
	 * 从请求头中获取用户名和密码
	 */
	private class UserTemp {
		private String username;
		private String password;

		public UserTemp(String username, String password) {
			this.username = username;
			this.password = password;
		}
	}

某J validateLogin二次验证用户并赋予token(不是该框架的可以忽略,是的话官方有代码)

package org.jeecg.modules.cas.controller;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.cas.util.CasServiceUtil;
import org.jeecg.modules.cas.util.XmlUtils;
import org.jeecg.modules.system.entity.SysDepart;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysDepartService;
import org.jeecg.modules.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;

import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * CAS单点登录客户端登录认证
 * </p>
 *
 * @Author zfq
 * @since 2023-12-20
 */
@Slf4j
@RestController
@RequestMapping("/sys/cas/client")
public class CasClientController {

	@Autowired
	private ISysUserService sysUserService;
	@Autowired
    private ISysDepartService sysDepartService;
	@Autowired
    private RedisUtil redisUtil;
	
	@Value("${cas.prefixUrl}")
    private String prefixUrl;
	
	
	@GetMapping("/validateLogin")
	public Object validateLogin(@RequestParam(name="ticket") String ticket,
								@RequestParam(name="service") String service,
								HttpServletRequest request,
								HttpServletResponse response) throws Exception {
		Result<JSONObject> result = new Result<JSONObject>();
		log.info("Rest api login.");
		try {
			String validateUrl = prefixUrl+"/p3/serviceValidate";
			String res = CasServiceUtil.getStValidate(validateUrl, ticket, service);
			log.info("res."+res);
			final String error = XmlUtils.getTextForElement(res, "authenticationFailure");
			if(StringUtils.isNotEmpty(error)) {
				throw new Exception(error);
			}
			final String principal = XmlUtils.getTextForElement(res, "user");
			if (StringUtils.isEmpty(principal)) {
	            throw new Exception("No principal was found in the response from the CAS server.");
	        }
			log.info("-------token----username---"+principal);
		    //1. 校验用户是否有效
	  		SysUser sysUser = sysUserService.getUserByName(principal);
	  		result = sysUserService.checkUserIsEffective(sysUser);
	  		if(!result.isSuccess()) {
	  			return result;
	  		}
	 		String token = JwtUtil.sign(sysUser.getUsername(), sysUser.getPassword());
	 		// 设置超时时间
	 		redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
	 		redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME*2 / 1000);

	 		//获取用户部门信息
			JSONObject obj = new JSONObject();
			List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
			obj.put("departs", departs);
			if (departs == null || departs.size() == 0) {
				obj.put("multi_depart", 0);
			} else if (departs.size() == 1) {
				sysUserService.updateUserDepart(principal, departs.get(0).getOrgCode(),null);
				obj.put("multi_depart", 1);
			} else {
				obj.put("multi_depart", 2);
			}
			obj.put("token", token);
			obj.put("userInfo", sysUser);
			result.setResult(obj);
			result.success("登录成功");
	  		
		} catch (Exception e) {
			//e.printStackTrace();
			result.error500(e.getMessage());
		}
		return new HttpEntity<>(result);
	}

	
}

6.某J前后端配置

  • 前端vue项目对接CAS

某J 前端3.0.0以后只默认集成SSO所有要的代码,只需需修改环境配置文件即可开启SSO

打开.env文件

#单点服务端地址
VITE_GLOBE_APP_CAS_BASE_URL=http://cas.test.com:8443/cas

# 是否开启单点登录
VITE_GLOB_APP_OPEN_SSO = false

复制

VITE_GLOB_APP_OPEN_SSO =true 即代表开启SSO登录

=============================

  • 后端boot修改yml配置
cas:
  # 配置CAS服务地址,cas为工程目录,部署到ROOT目录下http://cas.test.com:8443即可
  prefixUrl: http://cas.test.com:8443/cas 

6.最后启动各个服务访问前端:

一会跳到cas界面

输入完账号密码跳到首页但没内容,某j的bug感觉是

刷新完正常

7.补充单点登录后404问题解决

 在user.ts中找到:【QQYUN-8326】登录不需要构建路由,进入首页有构建---

打开注释

//update-begin---author:scott ---date::2024-02-21  for:【QQYUN-8326】登录不需要构建路由,进入首页有构建---
        // // 构建后台菜单路由
         const permissionStore = usePermissionStore();
         if (!permissionStore.isDynamicAddedRoute) {
           const routes = await permissionStore.buildRoutesAction();
           routes.forEach((route) => {
             router.addRoute(route as unknown as RouteRecordRaw);
           });
           router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw);
           permissionStore.setDynamicAddedRoute(true);
         }
        //update-end---author:scott ---date::2024-02-21  for:【QQYUN-8326】登录不需要构建路由,进入首页有构建---

参考博客:

cas5.3.2单点登录-rest认证(十二)_cas 调用其它接口认证-CSDN博客 这家写的cas真不错,十几篇成体系,比某些收费的都要好

https://www.cnblogs.com/youqc/p/14861455.html 这家的主要是入门免证书,htpp配置等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值