视频直播鉴权结合业务系统的token或session

前言

视频直播系统和Web后端系统基本是两套系统,借助Web后端的Shiro框架为视频直播提供鉴权可以实现非常细粒度的优秀鉴权。

■ 需求

现有两套系统,业务Web系统、视频流系统。视频流播放暂无鉴权,只要知道链接人人都可播放,现需要从业务系统获取内容判断是否拥有观看权限。

■ 解析

业务系统后端由Java编写,采用Shiro作为鉴权框架,下游使用Nginx作为负载均衡,业务系统里有视频直播功能,而真正提供视频流服务的是另一套系统。视频流系统是怎么实现的我们不需要去关心,只需要知道对外暴露的接口都通过Nginx代理了即可。现在需要做的就是调用视频流服务的接口(即播放等)时,调用业务系统的业务逻辑判断是否有播放权限,才能获取视频流。

分解步骤

① JavaWeb后端提供鉴权接口
② 视频流服务器的下游Nginx拦截播放请求,调用JavaWeb后端鉴权才可转发上游视频流服务器
③ 前端是否需要做些友好提示

开始实现

■ Nginx编译进ngx_http_auth_request_module模块

模块(1.5.4+)实现了基于一子请求的结果的客户端的授权。如果子请求返回2xx响应码,则允许访问。如果它返回401或403,则访问被拒绝并显示相应的错误代码。子请求返回的任何其他响应代码都被认为是错误的。
https://cloud.tencent.com/developer/section/1258993

通俗来说就是编译了这个模块后,Nginx在处理请求的时候可以调用另一个接口来鉴权,这个接口返回200响应码之类才会继续负载均衡之类,否则会返回错误码。

编译时添加参数

--with-http_auth_request_module 

Win版Nginx编译可参考
编译Windows版Nginx并添加模块

■ JavaWeb后端提供鉴权接口

在如今的Web项目中,通常认证 (authentication) 和授权 (authorization)失败后也是返回http200响应码,错误码在json中返回,所以shiro得做些调整,该接口得返回对应的http响应码,auth模块才能识别。
:authentication和authorization区别?
前者为认证,比如鉴定有没有token,token是否无效、失效,没有通过返回401。
后者为授权,比如token是有效的,但是你没有权限访问,返回403。

① authentication认证

仿这里的认证过滤器
shiro复用session实现前后端分离鉴权

其中过滤器倒二行,返回码设置401即可。

/**
 * 内部鉴权
 * @author bbq
 * @version 2021-06-28
 */
@Service
public class InternalAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {
    
    
	/**
		这里为自己的一套重写
 	*/
	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
    
    
		if (this.isLoginRequest(request, response)) {
    
    
			if (this.isLoginSubmission(request, response)) {
    
    
				if (log.isTraceEnabled()) {
    
    
					log.trace("Login submission detected.  Attempting to execute login.");
				}
				return this.executeLogin(request, response);
			} else {
    
    
				if (log.isTraceEnabled()) {
    
    
					log.trace("Login page view.");
				}
				return true;
			}
		} else {
    
    
			if (log.isTraceEnabled()) {
    
    
				log.trace("Attempting to access a path which requires authentication.  Forwarding to the Authentication url [" + this.getLoginUrl() + "]");
			}
			ResultDTO retDto = null;
			ErrorType et = ErrorType.getOperationType("ERROR_INT_TOKEN_INVALID");
			retDto = new ResultDTO(et.getResourceKey(), et.getType(), null);
			return responJson(response, retDto);
		}
	}

	// 直接返回json 并处理跨越
	public boolean responJson(ServletResponse response, ResultDTO retDto) throws IOException {
    
    
		HttpServletResponse httpServletResponse = (HttpServletResponse)response;
		httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
		httpServletResponse.setHeader("Access-Control-Allow-Methods", "*");
		httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
		httpServletResponse.setHeader("Access-Control-Allow-Headers",
				"Origin, X-Requested-With, Content-Type, Accept");
		httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); // 是否允许浏览器携带用户身份信息(cookie)
		response = httpServletResponse;
		response.getWriter().write(GsonUtils.toJson(retDto));
		((HttpServletResponse) response).setStatus(401);
		return false;
	}
}

② authorization授权

①中过滤器将internalauth接口都拦截了特殊处理。
shiro鉴权可粗可细,三档次。
粗粒度:第一个接口,只要有登录(认证通过),即授权成功。
中粒度:第二个接口,在此,我编写了一个通用接口供各式各样的内部鉴权使用,只要用户具有某个权限标识符,就可以授权成功。
细粒度:定制接口,可以查询数据库等等之类,鉴定更细的业务权限来决定是否授权。

/**
 * 内部鉴权Controller
 * @author bbq
 * @version 2021-6-28
 */
@Controller
@RequestMapping(value = "${adminPath}/sys/internalauth")
public class InternalAuthenticationController extends BaseController {
    
    

    @ResponseBody
    @RequestMapping(value = "base")
    public ResultDTO base( HttpServletRequest request, HttpServletResponse response) {
    
    
        try {
    
    
            return ResultDTO.buildResult(ResultDTO.SUCCESS_CODE, "成功返回");
        } catch (Exception e) {
    
    
            return ResultDTO.buildResult(ResultDTO.ERROR_CODE, "参数出错");
        }
    }

    @ResponseBody
    @RequestMapping(value = "advanced/{permitted}")
    public ResultDTO advanced(@PathVariable String permitted, HttpServletRequest request, HttpServletResponse response) {
    
    
        try {
    
    
        	// 即 SecurityUtils.getSubject();
            if (UserUtils.getSubject().isPermitted(permitted)) {
    
    
                return ResultDTO.buildResult(ResultDTO.SUCCESS_CODE, "成功授权");
            } else {
    
    
                response.setStatus(403);
                return ResultDTO.buildResult(ResultDTO.SUCCESS_CODE, "没有权限");
            }
        } catch (Exception e) {
    
    
            response.setStatus(403);
            return ResultDTO.buildResult(ResultDTO.ERROR_CODE, "参数出错");
        }
    }
}

■ Nginx配置内部鉴权

token可通过请求头传递也可以通过url参数传递

在这里有个坑,ng调用内部接口时$args、 $query_string 等为空,所以难以传递url参数,由于时间太久,我也忘了是什么原因,但是 $request_uri是带有参数的,并且internal中可以获取,采用正则方式处理一下 $request_uri将?后面参数剥离出来,再传递给java端的鉴权接口。

server {
    
    
		listen 9000;
		#listen 9001 ssl http2;
		server_name localhost;
	
		ssl相关配置。。。
		
    location /  {
    
       
		auth_request /internalfilter;
        error_page 401 = @error401;
        error_page 403 = @error403;
		
		proxy_pass http://127.0.0.1:8000/;
		proxy_set_header Upgrade $http_upgrade;    
		proxy_set_header Connection "Upgrade";    
		proxy_set_header X-real-ip $remote_addr;
		proxy_set_header X-Forwarded-For $remote_addr;
     }
	 
	location /internalfilter {
    
    
	    internal;
	    proxy_set_header Host $host;
	    proxy_pass_request_body off;
	    proxy_set_header Content-Length "";
		if ( $request_uri ~ ((\?)([\S\s]*))  ){
    
    
			set  $parameter $1;
		}
		
	    proxy_pass http://ip:8001/proxy/。。。/internalauth/XXXX$parameter;
    }
	 
	location @error401 {
    
    
	    return 401 $query_string"没有登录";
    }
	location @error403 {
    
    
	    return 403 "没有权限访问";
    }

■ postman8.5后的版本可以测试websocket

项目直播有hls、ws-flv方式,http如常鉴权,ws如例子和http类似,使用8.5以后版本的postman可以测试。
在这里插入图片描述

最后

最后,前端做些友好提示即可。这个方案不仅可以用在视频流上面,可以为各式各样的系统提供同一套的shiro鉴权,也方便管理。

猜你喜欢

转载自blog.csdn.net/qq_24054301/article/details/119247801