什么是动态代理
动态代理是指在不修改源代码的情况下,基于运行时生成的权限权限代理类和目标对象之间的代理关系,实现对目标对象方法的源码源码增强处理。以下是动态动态关于动态代理的详细解释:
一、动态代理的权限权限基本概念
动态代理是一种在运行时生成代理类和目标对象之间代理关系的机制。它不同于静态代理,源码源码工作任务管理 源码静态代理需要在编译时生成具体的动态动态代理类,而动态代理则是权限权限在运行时根据需要生成代理类。这种机制允许开发者在不修改目标对象源代码的源码源码情况下,对目标对象的动态动态方法进行增强处理,例如添加日志、权限权限事务管理、源码源码权限校验等。动态动态
二、权限权限动态代理的源码源码实现原理
动态代理的实现主要依赖于Java的反射机制。通过反射,代理类可以在运行时获取目标对象的接口、方法和相关属性信息。然后,代理类会在适当的时候调用目标对象的方法,并在调用前后添加自定义的逻辑。这样,就可以实现对目标对象方法的增强处理。
三、动态代理的应用场景
动态代理广泛应用于各种需要方法增强处理的情况。例如,在AOP中,锁机源码视频动态代理常被用于实现横切逻辑与目标业务逻辑之间的解耦。此外,在框架和库的设计中,动态代理也常被用于实现一些通用功能,如权限校验、缓存处理等。由于动态代理可以在运行时动态生成代理类,因此它具有很高的灵活性和可扩展性。
总之,动态代理是一种强大的技术,它允许开发者在不修改目标对象源代码的情况下,通过运行时生成的代理类实现对目标对象方法的增强处理。这种机制在面向切面编程、框架和库的设计等领域有着广泛的应用。
后台系统的权限控制与管理
前言
前端权限和后端权限的区别在哪儿?有了后端权限为什么还要前端权限?前端需要后端给出怎样的权限数据?本文围绕这三个问题,讲解了前端权限控制思路以及在Vue中的实现。
权限相关概念权限的分类后端权限从本质上来讲前端仅仅只是视图层的展示,权限的核心是在于服务器中的数据变化,所以后端才是权限的关键,后端权限可以控制某个用户是否能够查询数据,是否能够修改数据等操作。
目前被大家广泛采用的两种权限模型为:基于角色的访问控制(RBAC)和基于属性的访问控制(ABAC),二者各有优劣:
RBAC模型(用户、角色、权限)构建起来更加简单,缺点在于无法做到对资源细粒度地授权(都是ui登录界面源码授权某一类资源而不是授权某一个具体的资源)。
ABAC模型构建相对比较复杂,学习成本比较高,优点在于细粒度和根据上下文动态执行。
一般项目中使用的是RBAC模型。
前端权限前端权限的控制本质是控制前端视图层的展示和前端所发送的请求,但是前端的权限都是可以通过技术手段破解的,不能将系统安全寄希望于前端。
前端权限的意义如果仅从能够修改服务器中数据库的数据层面上讲,确实只在后端做控制就足够了,进行前端权限控制的好处主要如下:
仅展示用户权限内的内容,避免界面误导用户,提高用户体验
排除不必要的请求,减轻服务器压力
前端权限控制思路路由控制在登录请求中会得到权限的数据。前端根据权限去动态生成路由,只允许用户访问权限内的路由,如果通过地址栏去访问权限外的路由会重定向至页面(无论登录了还是没登录)。
按钮控制同一页面还可能因为权限不同展示不同的按钮。
请求和响应的控制如果用户通过非常规操作,比如通过浏览器调试工具将某些禁用的按钮变成启用状态。此时发的请求,也应该被前端所拦截。
Vue权限控制的实现获取权限信息一般权限信息会在用户登录后由后端返回,具体如何控制权限要看前端是如何与后端约定的。在查找资料并结合个人项目经验后,我总结有以下有三种常见的方式。
后端返回权限表或路由表这种方式常见于管理员可以添加新角色,gis监控系统源码并且要给这个角色分配菜单。拿到的数据大概如下:
{ "data":{ "id":1,"username":"admin","token":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTY1MzM1NzIyNH0.1y-Ucq_MfFRloesg0eA9pfk-VA3pV_zAOSj3HFpnKak"},"rights":[{ "id":,"authName":"用户管理","icon":"icon-user","children":[{ "id":,"authName":"用户列表","path":"users","rights":["view","edit","add","delete"]}]},{ "id":,"authName":"角色管理","icon":"icon-juese","rights":["view","edit","add","delete"]}]}data中是用户信息,其中重点关注token字段,需要在客户端对其进行持久化处理。
sessionStorage.setItem('token',res.token);在请求需要鉴权的接口时,需要将其放在请求头中传给后端。
权限表rights经过处理可以生成前端需要的路由表(这里展示的是父子结构,也可能拿到是一维数组,需要前端自己处理),如果返回的是路由表则无需额外的处理。
后端返回角色对于一些小项目来说,后端可能不想返回路由表,前端也觉得加一个页面要后端改一下太麻烦了。更常见的情况,后端会告诉前端用户的角色,数据大概如下:
{ "data":{ "id":1,"username":"admin","token":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTY1MzM1NzIyNH0.1y-Ucq_MfFRloesg0eA9pfk-VA3pV_zAOSj3HFpnKak","roles":['admin','editr']}}roles字段代表了用户的角色,用数组是因为一个用户可能有多个角色。路由表由前端来维护并根据角色动态生成。
前端根据用户信息判断角色查找的资料中这种情况比较少,但是我经常碰到这种情况,后端返回的用户信息如下所示:
{ "data":{ "id":1,"username":"admin","token":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTY1MzM1NzIyNH0.1y-Ucq_MfFRloesg0eA9pfk-VA3pV_zAOSj3HFpnKak","level":1,"units":{ "id":1,"name":"部门1"}}}一开始后端告诉你通过level字段判断权限,你很快写好了方法。随着业务的变动,产品提出了新的需求,增加了管理员,蓝鸟影视app源码后端告诉你:
当username为admin时角色是管理员。
过了一段时间,又告诉你:
当units为null时是特殊角色,只有导入数据的权限。
那可能会写出这样一个方法来判断用户的权限等级:
functiongetUserAuthority(){ constuserInfo=JSON.parse(sessionStorage.getItem('userData')).currentUserInfo;const{ username,units}=userInfo;if(units===null)return-1;if(username==='admin')return0;returnunits?.level;}尽管数据库中表还是原来的表,但是从前端的角度来看,假设原先level只有1、2、3三个等级,相当于三个角色,现在则是变成了5个角色。
路由控制的实现全局前置守卫判断是否登录先判断跳转的是不是登录页(或其他路由白名单中的页面)
是-->直接放行
否-->获取sessionStorage中的token,有则放行,空则跳转登录页
//router/index.jsrouter.beforeEach((to,from,next)=>{ //1.如果访问的是登录页面(无需权限),直接放行if(to.path==='/login')returnnext();//2.如果访问的是有登录权限的页面,先要获取tokenconsttoken=sessionStorage.getItem('token');//2.1如果token为空,强制跳转到登录页面;否则,直接放行if(!token){ Message.error('需要登录权限,请登录后再试');returnnext('/login');}returnnext();});动态路由一般来说vue-element-admin中路由分为两种:不需要权限判断的路由、需要动态判断权限的路由,下面就是不需要权限判断的路由。
constroutes=[{ path:'/',name:'Home',component:HomeLayout,redirect:'/menu/one',children:[]},{ path:'/login',name:'Login',component:Login},{ path:'*',name:'NotFound',component:NotFound},]constrouter=newVueRouter({ routes})动态路由处理的整体思路就是:根据权限生成路由,添加到路由表;当用户切换,权限发生改变时,清空动态添加的路由。
对于后端直接返回路由表,代码大概如下:
//路由拼接functionloadView(view){ return()=>import(`@/views/${ view}`)}//路由过滤和跳转asyncfunctiononFilterRoutes(to,next,e){ constroutes=awaitfilterASyncRoutes(e)//路由过滤routes.sort((a,b)=>a['id']-b['id'])routes.forEach(item=>{ router.options.routes.push(item)router.addRoute(item)})next({ ...to,replace:true})}//路由过滤遍历路由转换为组件对象和路径functionfilterASyncRoutes(data){ constroutes=data.filter(item=>{ if(item["component"]==="Layout")item.component=Layoutelseitem["component"]=loadView(item["component"])//路由递归,转换组件对象和路径if(item["children"]&&item["children"].length>0)item["children"]=filterASyncRoutes(item.children)returntrue})returnroutes}上面的代码参考自掘金-《vue实现用户登录验证+权限验证+动态路由(左侧菜单栏)》
不使用router.addRoutes方法是因为其在vuerouterv3.x中被废弃
对于后端返回权限表、后端返回角色信息、前端根据用户信息判断角色这三种情况,则稍微麻烦一些,需要经过映射转换生成路由后,再使用router.addRoute方法加入路由表。
映射转换的规则需要由前端来维护。
Tips:
另外,尽管VueRouter3.x的文档中提到:
当使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放在最后。
但经我实测后发现,含有通配符的路由放在最前面并不会影响后面路由的匹配。
按钮控制的实现最容易想到的就是v-if,但这样每有一个按钮就要写一段逻辑判断,不够简洁。考虑到复用性,可以定义自定义指令来封装逻辑,并给合路由元信息meta使用。
在根目录下新建directives/index.js,然后在main.js中引入,页面中使用v-permission即可,具体代码如下:
//页面中的使用,action控制展示,effect控制禁用v-permission="{ action:'add'}"v-permission="{ action:'delete',effect:'disabled'}"//directives/index.jsimportVuefrom'vue'Vue.directive('permission',{ inserted(el,binding){ const{ action,effect}=binding.value//根据路由中元信息meta来判断是否具备对应的权限if(router.currentRoute.meta.indexOf(action)===-1){ if(effect==='disabled'){ //如果是禁用添加'disable'样式el.disabled=trueel.classList.add('is-disabled')}else{ //否则就移除节点el.parentNode.removeChild(el)}}}})sessionStorage.setItem('token',res.token);0请求和响应的控制的实现请求携带token可以通过请求拦截器interceptors.request来携带token,代码如下:
sessionStorage.setItem('token',res.token);1token的校验导航守卫只能简单地对token的有无进行检查,并不能校验其合法性。
使用响应拦截器可以处理token不合法的情况(超时或篡改),具体代码如下:
sessionStorage.setItem('token',res.token);2拦截不应该发送的请求同样要使用请求拦截器,修改代码如下:
sessionStorage.setItem('token',res.token);3上面的代码对请求的类型进行映射出的权限与路由meta中的权限进行比较,然而实际开发中增删改查可能只用到get或者post,那么可能要将url作为key值,维护好actionMapping这个对象。
总结前端权限控制可以提高用户体验,减轻服务器压力,但是最后一道保障仍然是在后端。在做权限控制时总体思路是最重要的,好的思路能避免因业务的变动而修改大段代码。
参考bilibili-后台系统的权限控制与管理
掘金-vue实现用户登录验证+权限验证+动态路由(左侧菜单栏)
掘金-浅析vue-router源码和动态路由权限分配
原文:/post/#gStore-weekly | gStore源码解析(三):安全机制之用户权限管理解析
在gStore的全面安全机制中,用户权限管理是关键环节。首先,我们探讨权限的定义,它区分了系统用户(如system和root)和普通用户,后者的基本操作权限包括查询、更新等七类。用户权限在创建时需通过授权接口,针对特定数据库库进行定制化配置,这些信息会被存储在系统库中,并在ghttp服务启动时加载到用户对象中。
权限管理涉及动态调整,ghttp服务提供了新增、删除和清空权限的功能。新增权限通过ghttp::addPrivilege函数实现,删除和清空权限则通过ghttp::delPrivilege函数操作。权限校验在服务运行时进行,对用户操作进行验证,确保符合接口权限要求,系统用户默认拥有所有权限,而其他用户则在登录后只允许特定操作,如查看库信息和心跳检测。
理解这一部分后,建议配合gStore源码Main/ghttp.cpp进行深入研究。此外,gStore的安全机制还有更多内容等待探索,如黑白名单配置。如果你对gStore有任何疑问,可以添加运营人员微信,加入gStore图谱社区进行交流。
我们鼓励大家参与gStore-weekly技术文章征集活动,分享你的技术见解、案例或心得,原创文章有机会获得精美礼品。一起参与,共同提升gStore技术社区的活力和深度。
Gitea源码分析(一)
Gitea是一个基于Go编写的Git代码托管工具,源自于gogs项目,具有良好的后端框架和前端集成。
前端框架采用Fomantic UI和Vue,路由控制器框架在年4月从macaron切换到chi,形成了gitea项目的结构基础。
在调用接口时,gitea引入了'User','Repo','Org'等内容,简化了接口调用,便于管理。'ctx.User'和'ctx.Repo'内容动态变化,需要用户登录和进入仓库时赋值。
在'routers'下,'handler'相关文件分为'get'和'post'两类,前者涉及前端渲染,后者负责执行操作。
'get'请求通过'templates'中的文件渲染到前端,通过'ctx.Data["name"]'传递需要渲染的数据,获取URL参数使用'c.Query'。
'post'请求接收前端数据,通常通过'form'传值,从'context'生成,可以使用'form.xxx'直接调用,添加内容则需在'form'结构体中定义。
渲染生成网页使用'ctx.Html(,tplName)',根据'context'内容做条件判断。
权限管理功能实现中,数字越大权限越高,便于后续对比。'UnitType'包含多项,如仓库页面导航栏显示。检查权限时,对比AccessModeRead和模块权限,大于则认为具有读权限。
gitea默认运行于单一服务器,伸缩性有限。若需分布式改造,需解决大规模并发访问、存储库分片和数据库支撑等问题。通过ELB负载均衡分散到多个节点,数据库使用集群方案,但存储库分片面临巨大挑战,现有技术难以实现。
官方文档提供了其他开源库的介绍,包括配置文件、容器方式下的轻量仓库与CI使用方案等。深入研究可发现Gitea的配置、路由控制框架chi、权限管理实现及分布式架构改造思路。
2024-12-24 08:12
2024-12-24 08:10
2024-12-24 07:31
2024-12-24 06:44
2024-12-24 06:42