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传输过来的请求在访问数据库的时候传入指定的参数
实现
-
服务端
服务端支持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>
复制代码