1.jar包依赖必须的;
org.flowable
flowable-spring-boot-starter
6.5.0
org.flowable
flowable-ui-modeler-rest
6.5.0
org.flowable
flowable-ui-modeler-conf
6.5.0
org.flowable
flowable-ui-modeler-logic
6.5.0
2.配置可视化页面需要的接口;
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.DatabaseException;
import liquibase.resource.ClassLoaderResourceAccessor;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.flowable.ui.common.service.exception.InternalServerErrorException;
import org.flowable.ui.common.service.idm.RemoteIdmService;
import org.flowable.ui.modeler.properties.FlowableModelerAppProperties;
import org.flowable.ui.modeler.rest.app.EditorGroupsResource;
import org.flowable.ui.modeler.rest.app.EditorUsersResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.WebMvcRegistrations;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
/**
* flowable 配置类
* sy
*/
@Configuration
@EnableConfigurationProperties({FlowableModelerAppProperties.class})
@ComponentScan(basePackages = {
// "org.flowable.ui.modeler.conf", // conf
// "org.flowable.ui.modeler.security", // 授权
// "org.flowable.ui.common.security",// 授权
// "org.flowable.ui.common.conf", // flowable 开发环境内置的数据库连接
// "org.flowable.ui.common.filter", // IDM 方面的过滤器
"org.flowable.ui.modeler.repository",
"org.flowable.ui.modeler.service",
"org.flowable.ui.common.service",
"org.flowable.ui.common.repository",
"org.flowable.ui.common.tenant",
"org.flowable.ui.modeler.rest.app"
},
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = EditorUsersResource.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = EditorGroupsResource.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = RemoteIdmService.class)
})
@EnableAsync
public class AppDispatcherServletConfiguration implements WebMvcRegistrations {
private static final Logger LOGGER = LoggerFactory.getLogger(AppDispatcherServletConfiguration.class);
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
LOGGER.debug("初始化flowable...");
RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping();
requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
requestMappingHandlerMapping.setRemoveSemicolonContent(false);
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
Object[] interceptors = { localeChangeInterceptor };
requestMappingHandlerMapping.setInterceptors(interceptors);
return requestMappingHandlerMapping;
}
/**
* 重写Liquibase生成表方法,解决找不到ACT_DE_MODE报错
* @param dataSource
* @return
*/
@Bean
public Liquibase liquibase(DataSource dataSource) {
LOGGER.info("Liquibase 开始生成模板信息表");
Liquibase liquibase = null;
try {
DatabaseConnection connection = new JdbcConnection(dataSource.getConnection());
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection);
database.setDatabaseChangeLogTableName("ACT_DE_" + database.getDatabaseChangeLogTableName());
database.setDatabaseChangeLogLockTableName("ACT_DE_" + database.getDatabaseChangeLogLockTableName());
liquibase = new Liquibase("META-INF/liquibase/flowable-modeler-app-db-changelog.xml", new ClassLoaderResourceAccessor(), database);
liquibase.update("sy");
return liquibase;
} catch (Exception e) {
throw new InternalServerErrorException("生成模板表失败:", e);
} finally {
if (liquibase != null) {
Database database = liquibase.getDatabase();
if (database != null) {
try {
database.close();
} catch (DatabaseException e) {
LOGGER.warn("关闭Liquibase生成模板表失败:", e);
}
}
}
}
}
@Override
public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
return new RequestMappingHandlerAdapter();
}
@Override
public ExceptionHandlerExceptionResolver getExceptionHandlerExceptionResolver() {
return new ExceptionHandlerExceptionResolver();
}
}
3.伪造登陆信息(如果有集成权限框架可根据需要修改);
import com.google.common.cache.LoadingCache;
import org.flowable.idm.api.User;
import org.flowable.ui.common.filter.FlowableCookieFilterCallback;
import org.flowable.ui.common.model.RemoteToken;
import org.flowable.ui.common.model.RemoteUser;
import org.flowable.ui.common.security.FlowableAppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* FlowableFilter
* sy
*/
@Component
@WebFilter(urlPatterns = {"/app/**", "/api/**"})
public class FlowableFilter extends OncePerRequestFilter {
private Logger LOGGER = LoggerFactory.getLogger(FlowableFilter.class);
protected FlowableCookieFilterCallback filterCallback;
protected LoadingCache tokenCache;
/**
* 为flowable伪造用户信息
* @param request
* @param response
* @param filterChain
* @throws ServletException
* @throws IOException
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
RemoteToken token1 = new RemoteToken(); // 伪造token
token1.setId("KarkD0DrvtStMirNnlHOpw");
token1.setValue("kyBmsS7Nl+ScexbUbQk7GQ==");
token1.setId("123788798");
token1.setValue("567678");
token1.setUserId("admin");
RemoteToken token = token1;//getValidToken(request); // 正常应该从系统中获取
if (token != null) {
try {
User user = new RemoteUser(); // 伪造用户信息
user.setId("admin");
user.setFirstName("admin");
user.setLastName("admin");
user.setEmail("[email protected]");
List list = new ArrayList<>(); // 设置菜单权限(重要)
list.add(new SimpleGrantedAuthority("access-idm"));
list.add(new SimpleGrantedAuthority("access-rest-api"));
list.add(new SimpleGrantedAuthority("access-task"));
list.add(new SimpleGrantedAuthority("access-modeler"));
list.add(new SimpleGrantedAuthority("access-admin"));
FlowableAppUser appUser1= new FlowableAppUser(user,"admin",list);
FlowableAppUser appUser = appUser1;//userCache.get(token.getUserId());
// 设置用户信息
SecurityContextHolder.getContext().setAuthentication(new RememberMeAuthenticationToken(token.getId(),appUser, appUser.getAuthorities()));
} catch (Exception e) {
LOGGER.trace("token 获取失败:", e);
}
if (filterCallback != null) {
filterCallback.onValidTokenFound(request, response, token);
}
} else {
return;
}
try {
filterChain.doFilter(request, response);
} finally {
if (filterCallback != null) {
filterCallback.onFilterCleanup(request, response);
}
}
}
}
4.伪造获取用户信息接口/app/rest/account(有集成权限的可根据需要重写);
import org.flowable.ui.common.model.RemoteUser;
import org.flowable.ui.common.model.UserRepresentation;
import org.flowable.ui.common.security.FlowableAppUser;
import org.flowable.ui.common.security.SecurityUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* flowable 认证接口
* sy
*/
@RestController
public class DemoController {
/**
* 获取当前登陆用户信息
* @return
*/
@RequestMapping(value = "/app/rest/account", method = RequestMethod.GET, produces = "application/json")
public UserRepresentation getAccount() {
FlowableAppUser appUser = SecurityUtils.getCurrentFlowableAppUser();
UserRepresentation userRepresentation = new UserRepresentation(appUser.getUserObject());
if (appUser.getUserObject() instanceof RemoteUser) {
RemoteUser temp = (RemoteUser) appUser.getUserObject();
userRepresentation.setPrivileges(temp.getPrivileges());
}
return userRepresentation;
}
}
5.主入口文件排除登陆认证(可根据需要不设置)
//排除springsecurity认证
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
6.把flowable-6.5.0解压之后flowable-6.5.0\wars目录下解压flowable-modeler.war;
7.把flowable-modeler.war\WEB-INF\classes\static目录下的所有东西复制到项目的resources/static下的自定义目录下(这里以flowable-modeler为例)
8.配置文件;
#工作流配置
# 关闭异步,不关闭历史数据的插入就是异步的,会在同一个事物里面,无法回滚
# 开发可开启会提高些效率,上线需要关闭,开启后不会自动生成数据库表
flowable.async-executor-activate=false
#启用 flowable 端点
management.endpoint.flowable.enabled=false
# 是否需要自动部署流程定义。
flowable.check-process-definitions=false
# 需要添加至引擎的自定义Mybatis映射的FQN
#flowable.custom-mybatis-mappers=classpath:mappers/*.xml
# 设置页面字体
flowable.labelFontName=宋体
flowable.activityFontName=宋体
flowable.annotationFontName=宋体
# 禁用Liquibase
liquibase.enabled=false
# Liquibase自定义配置文件地址resources
#liquibase.change-log=classpath:/db/changelog/db.changelog-master.xml
# mybatis Flowable需要配置xml映射和configuration-properties三个属性
mybatis.mapper-locations=原来的配置,classpath:/META-INF/modeler-mybatis-mappings/*.xml
mybatis.configuration-properties.prefix=
mybatis.configuration-properties.blobType=BLOB
mybatis.configuration-properties.boolValue=TRUE
9.第一次启动会初始化数据库;
10.通过http://localhost:端口号/flowable-modeler/index.html访问即可;
遇到的问题:
1.org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): xxxx.xxx.xxx.xx;
找不到某个方法,解决方式在步骤2中