架构还是转到前后端分离,并且实现了多租户支持

最近无意中发现了一个IT培训教育的平台:慕课网。赶紧看了一些免费的我比较关心的课:shiro、mybatis-plus、vue入门等,立马对这些基础技术有了一个相对初步的了解,这种培训虽然是入门级的,但是怎么也比直接去某度上或者码云上去大海捞针强,真是师傅领进门、修行在个人,再次对慕课网平台,以及老猿等培训老师表示感谢。

回到我的平台研发,考虑到renren-security的前后端一体的项目,不能兼容手机访问,也实在是不想与前后端分离的大势相悖,所以经过又一段时间对renren-fast的vue版本的前后端代码分析和掌握,打算还是用回前后端分离模式,主要还是考虑到界面的因素,响应式布局以及vue的强大,相信会越来越方便。

另外基于平台中用到的mybatis-plus,很轻松的解决了我之前平台曾经实现过的多租户,再次感叹Java背后强大的开源力量。之前做多租户,是通过tenant_id关联到关键的业务数据,用户登录之后通过关联的业务数据,比如站点等,筛选业务数据。而mybtais-plus在分页插件里面通过统一的拦截实现,真的方便。在这里我记录一下,希望能够帮到后来的同行:

1,首先改造了renren框架,以便支持session,通过session保存的当前用户,可以关联找出当前的tenantid。改造内容包括:

OAuth2Filter中onLoginSuccess保存用户到session中:

    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
                                               ServletRequest request, ServletResponse response) throws Exception {
        //保存session
        Session session = SecurityUtils.getSubject().getSession();
        if(session!=null){
            SysUserEntity user = (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
            session.setAttribute("current_user",user);
            System.out.println("当前登录用户:"+session.getAttribute("current_user"));
        }
        return true;
    }

改造 isAcccessAllowed的方法,不再统一返回false,因为之前是所有的请求都重新登录效率太低了:

@Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if(((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())){
            return true;
        }
        return super.isAccessAllowed(request,response,mappedValue);
    }

登出logout方法增加代码,清空session:

        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        session.stop();
        subject.logout();

2,ShiroUtils增加方法,获取当前租户id:

	public static Integer getCurrentTenentId(){
		Subject subject = SecurityUtils.getSubject();
		Session session = subject.getSession();
		if(session!=null){
			SysUserEntity user = (SysUserEntity)session.getAttribute("current_user");
			if(user!=null){
				Integer tenantId = user.getTenantId();
				if(user.getUserId()==1){//如果是admin,从配置信息中读取当前激活tenant
					SysConfigService sysConfigService = (SysConfigService) SpringContextUtils.getBean("sysConfigService");
					tenantId = sysConfigService.getConfigObject(ConfigConstant.CURRENT_TENANT_CONFIG_KEY, Integer.class);
				}
				return tenantId;
			}
		}
		return null;
	}

普通用户是直接在user表增加了tenant_id字段,进行关联。对于超级管理员admin,是通过系统参数设定当前的tenant_id。如果当前登录的是普通用户,直接取session中的user对象属性即可,对于admin用户,是获取系统参数,每次可以在参数模块中切换。

3,在mybatis-plus的分页插件中实现多租户过滤:

@Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        ArrayList <ISqlParser> iSqlParserList = new ArrayList <ISqlParser>();
        TenantSqlParser tenantSqlParser = new TenantSqlParser();
        tenantSqlParser.setTenantHandler(new TenantHandler() {
            @Override
            public Expression getTenantId(boolean where) {
                Integer currentTenentId = ShiroUtils.getCurrentTenentId();
                return new LongValue(currentTenentId);
            }

            @Override
            public String getTenantIdColumn() {
                return "tenant_id";
            }

            @Override
            public boolean doTableFilter(String tableName) {
                if("schedule_job".equalsIgnoreCase(tableName)||
                        "sys_tenant".equalsIgnoreCase(tableName)||
                        "sys_captcha".equalsIgnoreCase(tableName)||
                        "sys_config".equalsIgnoreCase(tableName)||
                        "sys_user".equalsIgnoreCase(tableName)||
                        "sys_user_role".equalsIgnoreCase(tableName)||
                        "sys_user_token".equalsIgnoreCase(tableName)||
                        "sys_menu".equalsIgnoreCase(tableName)||
                        "sys_role".equalsIgnoreCase(tableName)||
                        "sys_role_menu".equalsIgnoreCase(tableName)){
                    return true;
                }
                return false;
            }
        });
        iSqlParserList.add(tenantSqlParser);
        paginationInterceptor.setSqlParserList(iSqlParserList);

        return paginationInterceptor;
    }

其中,doTableFilter方法可以过滤掉不需要筛选租户id的表,其余的都将通过TenantSqlParser进行自动过滤,影响到的具体功能包括insert、update、select等方法。

改完之后效果还是比较满意的,普通用户感觉不到租户的存在,对于admin超级管理员,也可以切换到不同租户进行数据管理。

 

 

上一篇:SpringBoot-实现图片上传 | 阿里云OSS


下一篇:获取IP地址工具类