SSO单点登录方案大全

分布式微服务系统主流常用的登录方案

前言: 单点登录其实是一个概念,主要是为了解决一次登录,多系统(本系统或外部系统)之间不需要重复登录的问题,就目前来说,主流的解决方案针对业务场景分为3个方向:

1: 同一公司,同父域下的单点登录解决方案.

如[http://map.baidu.com][[http://www.baidu.com][[http://image.baidu.com]
基于cookie开源项目代表: JWT;会详细介绍和实现;

2: 同一公司,不同域下的单点登录解决方案.

如[[http://www.taobao.com] [[http://www.tmall.com]]

基于中央认证服务器开源项目代表:CAS(https://github.com/apereo/cas); https://www.apereo.org/projects/cas

3: 不同公司之间,不同域下的 第三方登录功能实现.

如第三方网站支持qq登录,微信登录,微博登录等;

基于OAuth2.0协议各大公司自己的支持;

一: 基于JWT的单点登录解决方案:

首先我们看一下图形,这个就是基于JWT解决方案的单点登录解决方案;

img )

应用和原理 : 什么是JWT,什么业务场景使用JWT?

JWT:
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
JSONWeb令牌是一种开放的、行业标准的RFC7519方法,用于在双方之间安全地表示声明

JWT 应用场景:

Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用,JWT 省去了服务器存储用户信息的过程。

Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

JWT的令牌结构 看官方文档https://jwt.io/introduction 并解释

JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:Header,Payload,Signature。

官网对于这个三部分的定义。一个典型的JWT Token是 tttttttt.yyyyyyyyyyy.eeeeeee 这样组成。

img img

Header : 由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。然后,用Base64对这个JSON编码就得到JWT的第一部分。

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。分为三种类型

1:registered claims。标准声明。一般常用于校验的有 iat,exp,nbf 校验 token 是否过期。

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

2:public claims。公开声明。 可以存放任意信息,比如userid等。不是信息安全的。

3:private claims。私密声明。 双方约定的信息。

Signature Base64(header).Base64(payload) + head中定义的算法 + 密钥 生成一个字符串 。

使用方式:

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

*// Authorization: Bearer *

另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。

通过代码实现JWT之单点登录(基于springboot+dubbo+zookeeper)

代码实现JWT服务器(基于dubbo实现):

JWTService:

package com.qianfeng.user;import com.qianfeng.user.dto.UserRequest;
import com.qianfeng.user.dto.UserResponse;/*** \* 用户服务对外接口* <p>* \* @author Martin*/
public interface IUserService {/*** \* 用户登录接口* \* @param request user info* \* @return 查询到的结果*/UserResponse login(UserRequest request);
}

JWT ServiceImpl:

package com.qianfeng.user;import com.qianfeng.user.dao.entity.UserEntity;
import com.qianfeng.user.dao.mapper.UserMapper;
import com.qianfeng.user.dto.UserRequest;
import com.qianfeng.user.dto.UserResponse;
import com.qianfeng.user.util.JWTTokenUtil;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Map;/*** \* 用户业务实现类* \* @author Martin*/
@Service("userServiceImpl")
public class UserServiceImpl implements IUserService {undefined@Autowiredprivate UserMapper userMapper;/*** \* 用户登录方法实现* \* @param request 请求参数* \* @return 登录结果*/public UserResponse login(UserRequest request) {undefined//非空校验UserResponse ur = verification(request);if (null != ur) {undefinedreturn ur;}UserEntity ue = new UserEntity();ue.setUserName(request.getUserName());ue.setUserPass(request.getUserPass());//执行登录UserEntity user = userMapper.login(ue);ur = new UserResponse();if (null == user) { //登录失败ur.setCode(ErrorCode.CODE_LOGIN_FAIL);ur.setMsg(ErrorCode.MSG_LOGIN_FAIL);return ur;}//获取tokenMap<String, Object> map = new HashMap<>();map.put("uid", user.getUserID());map.put("exp", DateTime.now().plusSeconds(30).toDate().getTime() / 1000); //设置过期时间 当前时间的基础上加上30sString token = JWTTokenUtil.generatorToken(map); //通过封装的算法来获取tokenur.setToken(token);ur.setUid(user.getUserID());//将一些用户信息返回,可以封装一个通用类return ur;}//非空校验private UserResponse verification(UserRequest request) {undefinedUserResponse ur = null;//请求为空if (null == request) {undefinedur = new UserResponse();ur.setCode(ErrorCode.CODE_ERROR_SYSTEM);ur.setMsg(ErrorCode.MSG_ERROR_SYSTEM);return ur;}//用户名为空if (StringUtils.isEmpty(request.getUserName())) {undefinedur = new UserResponse();ur.setCode(ErrorCode.CODE_USER_EMPTY);ur.setMsg(ErrorCode.MSG_USER_EMPTY);return ur;}//密码为空if (StringUtils.isEmpty(request.getUserPass())) {undefinedur = new UserResponse();ur.setCode(ErrorCode.CODE_PASS_EMPTY);ur.setMsg(ErrorCode.MSG_PASS_EMPTY);return ur;}return ur;}
}

JWT Util:

package com.qianfeng.user.util;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Map;/*** \* JWT 生成token和提取token信息工具类* \* @author Martin*/
public class JWTTokenUtil {undefinedstatic SignatureAlgorithm sa = SignatureAlgorithm.HS256;//使用hs256算法//获取keyprivate static Key generatorKey() {undefinedbyte[] bin = DatatypeConverter.parseBase64Binary("2521afcf18c749c1a8a7615c03d15e43");Key key = new SecretKeySpec(bin, sa.getJcaName());return key;}/*** \* 将传过来的信息按照 Header,Payload,Signature 的方式组装一个字符串* \* @param payLoad* \* @return*/public static String generatorToken(Map<String, Object> payLoad) {undefinedObjectMapper objectMapper = new ObjectMapper();try {undefinedreturn Jwts.builder().setPayload(objectMapper.writeValueAsString(payLoad)).signWith(sa, generatorKey()).compact();} catch (JsonProcessingException e) {undefinede.printStackTrace();}return null;}/*** \* 将token解析,得到Payload内容* \* @param token* \* @return*/public static Claims phaseToken(String token) {undefined//将token解析成claimsJws<Claims> jws = Jwts.parser().setSigningKey(generatorKey()).parseClaimsJws(token);// jws.getHeader(); Header
// jws.getBody(); Payload
// jws.getSignature(); Signaturereturn jws.getBody();}}

代码实现JWT客户端请求:

tocken校验拦截器:

package com.qianfeng.conf.interceptor;import com.qianfeng.user.anno.Evade;
import com.qianfeng.user.util.CookieUtil;
import com.qianfeng.user.util.JWTTokenUtil;
import com.qianfeng.user.web.BaseController;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.SignatureException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;/*** \* 拦截器,如果没有token,直接返回结果。* \* @author Martin*/
public class TokenInterceptor implements HandlerInterceptor {undefined@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {undefinedif (handler instanceof ResourceHttpRequestHandler || handler instanceof AbstractErrorController) {undefinedreturn true;}System.out.println("start");HandlerMethod handlerMethod = (HandlerMethod) handler;//如果需要规避if (hasEvade(handlerMethod)) {undefinedSystem.out.println("evade : " + handlerMethod.toString());return true;}//从cookie中获取tokenString access_token = CookieUtil.getCookieValue(request, "access_token");//如果没有token,跳转到登录页面,这里需要判断一下是否是ajax请求if (StringUtils.isEmpty(access_token)) {undefinedSystem.out.println("token is null");return vafail(request, response);}Claims claims = null;try {undefinedclaims = JWTTokenUtil.phaseToken(access_token); //解析token} catch (ExpiredJwtException e) { //token已经过期System.out.println("token out time");return vafail(request, response);} catch (SignatureException e1) { //签名校验失败System.out.println("token signature fail");return vafail(request, response);}if (null != claims && !StringUtils.isEmpty(claims.get("uid").toString())) {undefinedSystem.out.println("token success");//获取id,并将id设置到Controllerif (handlerMethod.getBean() instanceof BaseController) {undefinedBaseController bean = (BaseController) handlerMethod.getBean();bean.setUserID(claims.get("uid").toString());}return true;}System.out.println("end");return false;}private boolean vafail(HttpServletRequest request, HttpServletResponse response) throws Exception {undefinedif (CookieUtil.isAjax(request)) {undefinedresponse.getWriter().println("{'result':'6666','msg':'no token'}");return false;}response.sendRedirect("login");return false;}/*** \* 验证是否可以规避,不需要拦截* \* @param handlerMethod* \* @return*/private boolean hasEvade(HandlerMethod handlerMethod) {undefinedMethod method = handlerMethod.getMethod();return method.getAnnotation(Evade.class) != null; //判断方法上面是否有自定义的规避注解。}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {undefined
// TODO}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {undefined
// TODO}
}

LoginController登录实现代码:

package com.qianfeng.user.web;import com.alibaba.dubbo.config.annotation.Reference;
import com.qianfeng.user.anno.Evade;
import com.qianfeng.user.dto.UserRequest;
import com.qianfeng.user.dto.UserResponse;
import com.qianfeng.user.IUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** \* 登录* \* @author Martin*/
@Controller
public class LoginController extends BaseController {undefined@Reference //通过dubbo注入实现类IUserService userService;@GetMapping("/login")@Evade //自定义注解,配置该注解不拦截请求;public String jumpLogin() {undefinedreturn "login";}@RequestMapping(value = "/dologin")@Evadepublic String doLogin(UserRequest ur, HttpServletRequest request, HttpServletResponse response) {undefinedUserRequest res = new UserRequest(ur.getUserName(), ur.getUserPass());if (null == userService) { //dubbo服务不可用return "error";}UserResponse rp = userService.login(res); //通过dubbo调用用户服务进行登录//登录失败跳转到登录页面if (null == response || StringUtils.isEmpty(rp.getUid())) {undefinedreturn "login";}//按照以前,如果登录成功,是需要将用户信息保存到session中。request.getSession().setAttribute("user", rp);request.setAttribute("uid", rp.getUid());//使用token的方式,将信息保存到客户端 access_token : 注意,这里在获取的时候也需要这个值
//response.addHeader("Set-Cookie","access_token="+rp.getToken()+";Path=/;HttpOnly"); //根目录的 cookie 设置tokenreturn "index";}//用来测试,是否可以直接访问资源@RequestMapping(value = "/buy")public String buy(HttpServletRequest request) {//从Session中获取用户信息UserResponse ur = (UserResponse) request.getSession().getAttribute("user");System.out.println("Session中: /buy : id " + ur.getUid());// System.out.println("Token中:/buy : id " + getUserID());
// request.setAttribute("uid",getUserID());return "info";}}

Evade 自定义注解配置不需要验证的请求:

package com.qianfeng.user.anno;import java.lang.annotation.*;/*** \* 自定义注解,该注解可以设置不拦截处理* \* @author Martin*/
@Documented
@Inherited
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Evade {}

以上为主要实现代码

二: 基于CAS同公司不同域的单点登录解决方案

Yelu大学研发的CAS单点登录原理:
img !

主要步骤分为:

1:用户请求CAS Client的某一个网站,或者直接请求CAS Server登录页面;

2:用户到CAS Server的登录页面进行验证,验证成功,CAS Server返回ticket到客户端;

3:客户端再次请求CAS Client资源, CAS Client携带ticket到CAS Server认证,成功则继续访问;

实现步骤:

1: 搭建CAS Server端,可使用CAS的框架实现;可自定义登录页面;实现其内部接口即可;

2: 搭建CAS Client端,就是公司不同的服务器系统,携带秘钥即可;

三: 基于Auth2.0之第三方登录

OAuth 2.0是用于授权的行业标准协议。
OAuth 2.0 is the industry-standard protocol for authorization
OAuth定义了四个角色:

  • 资源所有者
    能够授予对受保护资源的访问权限的实体。
    当资源所有者是一个人时,它被称为
    最终用户。

  • 资源服务器
    托管受保护资源的服务器,能够接受
    并使用访问令牌响应受保护的资源请求。

  • 客户
    代表受保护的资源请求的应用程序
    资源所有者及其授权。“客户”一词的确如此
    并不意味着任何特定的实施特征(例如,
    应用程序是在服务器,桌面还是其他服务器上执行
    设备)。

  • 授权服务器
    服务器成功后向客户端发出访问令牌
    验证资源所有者并获得授权。

授权模式

1 简化模式(Implicit)

2 授权码模式(Authorization Code)

3 密码模式(Resource Owner Password Credentials Grant)

4 客户端模式(Client Credentials)

场景: 公司的网站支持第三方登录(qq登录,微信登录,微博登录):

qq开放平台:https://connect.qq.com/index.html

微博开放平台: https://open.weibo.com/authentication/

接入方式按照对应的平台操作即可,第三方网站接入文档提示非常全面.

关于登录,还请题主需要关注一下 分布式下登录成功以后,分布式Session的问题以及解决方案;

方案是用来解决问题的,这些方案没有好不好的说法,只有合不合适的说法,合适的才是最好的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556969.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

em算法怎么对应原有分类_机器学习基础-EM算法

EM算法也称期望最大化(Expectation-Maximum,简称EM)算法&#xff0c;它是一个基础算法&#xff0c;是很多机器学习领域算法的基础&#xff0c;比如隐式马尔科夫算法(HMM)&#xff0c; LDA主题模型的变分推断等等。本文就对EM算法的原理做一个总结。EM算法要解决的问题我们经常会…

postman插件下载、安装教程

这里只讲如何在Chrome 中安装postman插件 下载链接&#xff1a;https://pan.baidu.com/s/1vampHeD0UiDNbrB3G8j_hA 提取码&#xff1a;wqdl 方法/步骤 1.在Chrome输入地址&#xff1a;[chrome://extensions/] 2.将压缩包直接拖拽至Chrome中 3.运行在Chrome输入地址&#xff…

得力条码扫描器怎么用_广东智能物流控制系统怎么选

广东智能物流控制系统怎么选&#xff0c;东莞智库&#xff0c;东莞智库(SmartWarehouse)&#xff0c;专注电子制造SMT智能仓库&#xff0c;致力于帮助电子制造企业提高物流仓储效率和效能。广东智能物流控制系统怎么选&#xff0c; 旭日东自动分拣系统是个集机械、电气、计算机…

从mysql到大数据(三)--mysql数据库建模一常用数据类型及引擎

数据库的安装请自行百度。如果你想直捣黄龙练查询&#xff0c;没有表没有数据是不能实现的。我们从建表开始学习。但要知道&#xff0c;我们所有东西都是了解&#xff0c;学习不要有压力&#xff0c;不要必须要求记什么&#xff0c;当然&#xff0c;如果你不累可以记&#xff0…

Postman用法简介-Http请求模拟工具

Postman用法简介-Http请求模拟工具 在我们平时开发中&#xff0c;特别是需要与接口打交道时&#xff0c;无论是写接口还是用接口&#xff0c;拿到接口后肯定都得提前测试一下&#xff0c;这样的话就非常需要有一个比较给力的Http请求模拟工具&#xff0c;现在流行的这种工具也…

matlab多元函数_函数的计算机处理8(1)_1MATLAB

计算机语言运用--数值计算8-函数的计算机处理8(1)_1MATLAB计算机&#xff1a;电子线路组成的计算机器。人与计算机则是通过计算机语言-符号系统说给计算机听而交流。计算机语言有低级语言-机器语言、汇编、高级语言-C/C/C#/VB/PASCAL/LISP/JAVA/PYTHON/……成百上千种之多。 作…

java 怎么通过url获取远程服务器上某个文件夹下的所有文件_JMX远程代码漏洞研究...

前言&#xff1a;前一段时间apace solr JMX因为配置不当出现远程代码执行漏洞&#xff0c;最近自己在看一套java系统时&#xff0c;发现该系统也存在JMX远程代码漏洞&#xff0c;于是乎就想研究下JMX这种通用型漏洞&#xff0c;下面我就从原理到利用对该漏洞做一个简单的梳理。…

app每秒并发数_性能测试连载 (38) jmeter 线程数与性能测试的负载模式

点击跳转>>jmeter--由浅入深学性能系列需求下面有3个场景&#xff0c;思考一下在jmeter里面如何设计场景1&#xff1a;有一个项目&#xff0c;500用户同时登录&#xff0c;响应时间能达到多少场景2&#xff1a;考勤打卡&#xff0c;最大吞吐量能达到多少(每秒最大能完成多…

用自定义注解做点什么——自定义注解有什么用

用自定义注解做点什么 前言 你不一定听过注解&#xff0c;但你一定对Override不陌生。 当我们重写父类方法的时候我们就看到了Override。我们知道它表示父类方法被子类重写了。 现在告诉你&#xff0c;Override就是一个注解。 也许你会疑惑注解是什么&#xff1f; 注解&…

c++ 查找文件夹下最新创建的文件_云计算开发总结:搜索Linux文件和文件夹的方法...

当下&#xff0c;随着Linux在物联网、云技术、超级计算和人工智能等领域扮演关键角色&#xff0c;各种会议和新版本的发布令人应接不暇&#xff0c;Linux将迎来一个激动人心的“云时代”。如果你想把握这个风口&#xff0c;现在是学习Linux技术的最佳时期。今天千锋广州云计算培…

RSA 非对称加密原理

RSA 加密原理 步骤说明描述备注1找出质数P 、Q-2计算公共模数N P * Q-3欧拉函数φ(N) (P-1)(Q-1)-4计算公钥E1 < E < φ(N)E的取值必须是整数 E 和 φ(N) 必须是互质数5计算私钥DE * D % φ(N) 1-6加密C &#xff1d; M E mod NC&#xff1a;密文 M&#xff1a;明文7…

浅谈对称加密与非对称加密

在数字加密算法中&#xff0c;通过可划分为对称加密和非对称加密。 一&#xff1a;什么是对称加密&#xff1f; 在对称加密算法中&#xff0c;加密和解密使用的是同一把钥匙&#xff0c;即&#xff1a;使用相同的密匙对同一密码进行加密和解密&#xff1b; 加密过程如下&…

ios跨线程通知_一种基于Metal、Vulkan多线程渲染能力的渲染架构

快手Y-tech 原创最新技术干货分享随着3D渲染场景规模越来越复杂&#xff0c;单线程渲染架构在满足业务性能要求时已经捉襟见肘&#xff0c;因此&#xff0c;多线程渲染显得愈发重要。本文首先介绍了新一代图形渲染接口Metal、Vulkan&#xff0c;以及它们的多线程渲染特性&…

58同城面试盘点

58同城面试盘点 1.一张订单表&#xff0c;有user_name,order_id,order_time,order_amount 四个字段&#xff0c;怎么取出每个用户2021年10月以来第一个订单的金额&#xff08;下单时间格式为’yyyy-MM-dd HH:mm:ss’&#xff09;&#xff1f; select user_name,order_id,orde…

stringbuffer判断是否为空

StringBuffer sbnew StringBuffer();if(sb!null && sb.length()>0){System.out.println("证明sb不为空!"); }

virtualbox: win11主机安装deepin双向复制问题

virtualbox: win11主机安装deepin双向复制问题1.安装virtualbox增强组件(确保光驱可用)2.终端挂载3. 运行BoxLinuxAdditions4. 重启虚拟机&#xff0c;验证OK&#xff01;使用virtualbox安装深度系统deepin虚拟&#xff0c;发现虚拟机和宿主机之间不能双向复制&#xff0c;已经…

基坑监测日报模板_刚刚!温州瓯海突发塌陷,初步判断为临近地块地下室基坑支护桩移位...

资料来源&#xff1a;瓯海新闻网 | 温州百事通 | 土木吧 | 岩土新鲜事 等版权归原作者所有如有侵权请联系删除9月10日中午11点左右&#xff0c;温州市瓯海区娄桥街道商汇路道路塌陷。塌陷路面位于商汇路的公交车站旁&#xff0c;几十米长的路面已经开裂&#xff0c;公交站台发生…

java 从一个总的list集合中,去掉指定的集合元素,得到新的集合——removeAll()

/*** 两个list集合的差集* author*/ public class ListSubstract {public static void main(String[] args) {List<String> list new ArrayList<>();//作为总的listList<String> existList new ArrayList<>();//存在的listlist.add("aa");…

virtualbox:win11上的deepin如何设置与宿主机共享文件

1. 安装virtualbox增强功能 这个没有测试&#xff0c;只是理论上需要。我在上一篇帖子《virtualbox: win11主机安装deepin双向复制问题》已经安装了增强功能&#xff0c;大家可以参考安装。 2.在virtualbox上配置共享 2.1 关闭虚拟机&#xff0c;进行设置 共享文件夹路径点击…

三角形外接球万能公式_秒杀三角形问题!!三角形分角线的几个重要结论及其应用...

点击“高中数学资料共享”关注我们解三角形问题在高考中的选择、填空、解答题一般都会涉及到(最少也有两块涉及到)&#xff0c;其中有一类涉及角平分线长度、中线长、高线长度问题&#xff0c;难度不大&#xff0c;但运算量不小&#xff0c;那我们如果在考试中能在最短时间内把…