RMI鉴权

本文介绍了一种通过RMI传输指定用户信息的方法,以实现对MyBatis查询逻辑的改造,确保数据库访问时能传递特定参数。文中详细描述了服务端和客户端的具体实现过程。

title: RMI鉴权 tags:

  • rmi
  • 授权 categories: 工作日志 date: 2016-10-25 18:18:54

关于使用spring自动扫描发布rmi详细请阅读把大象放进冰箱--spring自动扫描并发布rmi

下面详细描述一下rmi鉴权相关内容

背景

由于大量业务查询需要和用户进行绑定,因此需要对mybatis中传入指定用户的信息比如用户所在的门店,为了避免传入大量的共通字段,改造了mybatis 的查询逻辑

在需要传入机构Id的sql处通过修改动态context获得 ,升级mybatis之后目前替换成bind机制

因此需要rmi传输过来的请求在访问数据库的时候传入指定的参数

实现

  1. 服务端

    服务端支持rmi鉴权,利用了DefaultRemoteInvocationExecutor,需要继承invoke方法,在执行invoke之前check

是否传输clientInfo

    public class ClientInfoRemoteInvocationExecutor extends DefaultRemoteInvocationExecutor {
     
        @Autowired
        private OrgGroupService orgGroupService;
     
        @Override
        public Object invoke(RemoteInvocation invocation, Object targetObject) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
            try {
                ClientInfo clientInfo = (ClientInfo) invocation.getAttribute(ClientInfoRemoteInvocationFactory.CLIENT_INFO);
                if (clientInfo != null) {
                    ClientInfo.checkClientInfo(clientInfo);
                    WxbStatic.setUser(clientInfo.getUser());
                    WxbStatic.setOrg(clientInfo.getIdOwnOrg());
                    WxbStatic.setIdsOwnOrg(cloneSet(orgGroupService.getTotalOrgIds(clientInfo.getIdOwnOrg())));
                    try {
                        WxbStatic.setIp(clientInfo.getRemoteIp() == null ? RemoteServer.getClientHost() : clientInfo.getRemoteIp());
                    } catch (ServerNotActiveException ignore) {
     
                    }
                }
                return super.invoke(invocation, targetObject);
            } finally {
                WxbStatic.clearThreadLocal();
            }
        }
    }
复制代码

在发布rmi时候指定对应executor

    <bean class="org.springframework.remoting.rmi.RmiServiceExporter">
        <property name="serviceName" value="erpReservationService" />
        <property name="service" ref="reservationRmiImpl" />
        <property name="serviceInterface" value="com.air.tqb.rmi.ReservationRmi" />
        <property name="registryPort" value="${erp.rmi.base.port}" />
        <property name="remoteInvocationExecutor" ref="clientInfoRemoteInvocationExecutor"/>
    </bean>
复制代码

客户端覆盖自定义生成remoteInvocation

2. 客户端

    /**
     * Created by qixiaobo on 16/9/20.
     */
    public class ClientInfoRemoteInvocationFactory extends DefaultRemoteInvocationFactory {
        public static final String CLIENT_INFO = "_clientInfo";
        private ThreadLocal<ClientInfo> clientInfoTL = new ThreadLocal<ClientInfo>();
        private RemoteInvocationCallback remoteInvocationCallback;
     
        public RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
            remoteInvocationCallback.beforeCreateRemoteInvocation(this);
            RemoteInvocation remoteInvocation = super.createRemoteInvocation(methodInvocation);
            if (getClientInfo() != null) {
                remoteInvocation.addAttribute(CLIENT_INFO, DozerHelper.clone(getClientInfo()));
            }
            clientInfoTL.remove();
            remoteInvocationCallback.afterCreateRemoteInvocation(this);
            return remoteInvocation;
        }
     
        public ClientInfo getClientInfo() {
            return clientInfoTL.get();
        }
     
        public void setClientInfo(ClientInfo clientInfo) {
            clientInfoTL.set(clientInfo);
        }
     
        public RemoteInvocationCallback getRemoteInvocationCallback() {
            return remoteInvocationCallback;
        }
     
        public void setRemoteInvocationCallback(RemoteInvocationCallback remoteInvocationCallback) {
            this.remoteInvocationCallback = remoteInvocationCallback;
        }
    }
复制代码

开发者需要自定义RemoteInvocationCallback的实现,并将需要的clientInfo传入,此处需要注意竞态条件

ex:

    /**
     * Created by qixiaobo on 2016/11/8.
     */
    public class DefaultRemoteInvocationCallback implements RemoteInvocationCallback {
        public void afterCreateRemoteInvocation(ClientInfoRemoteInvocationFactory clientInfoRemoteInvocationFactory) {
     
        }
     
        public void beforeCreateRemoteInvocation(ClientInfoRemoteInvocationFactory clientInfoRemoteInvocationFactory) {
            ClientInfo clientInfo = new ClientInfo();
            clientInfo.setIdOwnOrg(WxbStatic.getOrg());
            clientInfo.setUser(WxbStatic.getUser());
            clientInfo.setRemoteIp(WxbStatic.getIp());
            clientInfoRemoteInvocationFactory.setClientInfo(clientInfo);
        }
    }
复制代码

在xml中定义需要使用的rmi如下

    <bean id="f6OrderRmiService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
        <property name="serviceUrl" value="${f6web.rmi.base.url}/f6OrderRmiService" />
        <property name="serviceInterface" value="models.rmi.F6OrderRmiService" />
        <property name="lookupStubOnStartup" value="false" />
        <property name="remoteInvocationFactory" ref="clientInfoRemoteInvocationFactory"/>
        <property name="refreshStubOnConnectFailure" value="true" />
    </bean>
    <bean id="clientInfoRemoteInvocationFactory" class="com.air.tqb.rmi.clientInfo.ClientInfoRemoteInvocationFactory">
        <property name="remoteInvocationCallback" ref="remoteInvocationCallback"/>
    </bean>
    <bean id="remoteInvocationCallback" class="com.air.tqb.rmi.clientInfo.DefaultRemoteInvocationCallback"/>
复制代码

注意此处自定义了remoteInvocationFactory,这样就可以使用自己够早的factory创建remoteInvocation,在其中附加了本次rmi请求携带的clientInfo,当服务端接收到该请求后可以决定授权与否。比如可以定义通信必须要求加密token,如果不符合请求则failfast

具体mybatis使用时include即可

    <sql id="c_select_with_id_own_org">
    		<bind name="_ids_own_org" value="@com.air.tqb.utils.WxbStatic@getIdsOwnOrg()"/>
    		<if test="@Ognl@isNotEmpty(_ids_own_org)">
    			<foreach item="_idOwnOrg" index="index" collection="_ids_own_org" open="(" close=")"  separator=",">
    				CAST(#{_idOwnOrg}  as unsigned )
    			</foreach>
    		</if>
    		<if test="@Ognl@isEmpty(_ids_own_org)">
    			(9999)
    		</if>
    	</sql>
复制代码

17-Oct-2025 09:15:24.941 严重 [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.core.StandardContext.listenerStart 异常将上下文初始化事件发送到类的侦听器实例.[org.springframework.web.context.ContextLoaderListener] java.lang.IllegalStateException: No such extension dubboProviderFilterId for filter/org.apache.dubbo.rpc.Filter,org.apache.dubbo.rpc.cluster.filter.ClusterFilter at org.apache.dubbo.config.utils.ConfigValidationUtils.checkMultiExtension(ConfigValidationUtils.java:731) at org.apache.dubbo.config.utils.ConfigValidationUtils.validateAbstractInterfaceConfig(ConfigValidationUtils.java:424) at org.apache.dubbo.config.utils.ConfigValidationUtils.validateServiceConfig(ConfigValidationUtils.java:445) at org.apache.dubbo.config.ServiceConfig.checkAndUpdateSubConfigs(ServiceConfig.java:519) at org.apache.dubbo.config.ServiceConfig.postProcessRefresh(ServiceConfig.java:526) at org.apache.dubbo.config.AbstractConfig.refresh(AbstractConfig.java:714) at org.apache.dubbo.config.deploy.DefaultModuleDeployer.exportServiceInternal(DefaultModuleDeployer.java:448) at org.apache.dubbo.config.deploy.DefaultModuleDeployer.exportServices(DefaultModuleDeployer.java:424) at org.apache.dubbo.config.deploy.DefaultModuleDeployer.startSync(DefaultModuleDeployer.java:174) at org.apache.dubbo.config.deploy.DefaultModuleDeployer.start(DefaultModuleDeployer.java:156) at org.apache.dubbo.config.spring.context.DubboDeployApplicationListener.onContextRefreshedEvent(DubboDeployApplicationListener.java:157) at org.apache.dubbo.config.spring.context.DubboDeployApplicationListener.onApplicationEvent(DubboDeployApplicationListener.java:143) at org.apache.dubbo.config.spring.context.DubboDeployApplicationListener.onApplicationEvent(DubboDeployApplicationListener.java:52) at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378) at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:940) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:399) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:278) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:103) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4701) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5167) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:743) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:719) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:705) at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1720) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:287) at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:479) at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:428) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:287) at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) at com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468) at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468) at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76) at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309) at java.security.AccessController.doPrivileged(Native Method) at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1408) at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357) at sun.rmi.transport.Transport$1.run(Transport.java:200) at sun.rmi.transport.Transport$1.run(Transport.java:197) at java.security.AccessController.doPrivileged(Native Method) at sun.rmi.transport.Transport.serviceCall(Transport.java:196) at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688) at java.security.AccessController.doPrivileged(Native Method) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) 17-Oct-2025 09:15:24.952 信息 [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.core.ApplicationContext.log Closing Spring root WebApplicationContext
最新发布
10-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值