准备:项目使用的是springboot2.1.5版本,flowable6.4.0版本
1. IDEA新建Spring Initializr项目,选择依赖,web和MySQL
2. 修改pom.xml依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.sgcc.flowable</groupId> <artifactId>mpur-flowable-service</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>mpur-flowable-service</name> <description>mpur-flowable-service</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <mysql.version>5.1.38</mysql.version> <flowable.version>6.4.0</flowable.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter</artifactId> <version>${flowable.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.54</version> <scope>compile</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.flowable/flowable-ui-modeler-logic --> <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-ui-modeler-logic</artifactId> <version>6.4.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.flowable/flowable-ui-modeler-conf --> <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-ui-modeler-conf</artifactId> <version>6.4.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3. 将下载的flowable6.4.0包中flowable-modeler.war解压,将flowable-modelerWEB-INFclasses目录下的static文件夹全部复制到项目中
4. 修改RemoteAccountResource类,保持包目录不变。
package org.flowable.ui.common.rest.idm.remote; import org.flowable.ui.common.model.UserRepresentation; import org.flowable.ui.common.security.DefaultPrivileges; import org.flowable.ui.common.service.exception.NotFoundException; import org.flowable.ui.common.service.idm.RemoteIdmService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; @RestController @RequestMapping("/app") public class RemoteAccountResource { @Autowired private RemoteIdmService remoteIdmService; /** * GET /rest/account -> get the current user. */ @RequestMapping(value = "/rest/account", method = RequestMethod.GET, produces = "application/json") public UserRepresentation getAccount() { // UserRepresentation userRepresentation = null; // String currentUserId = SecurityUtils.getCurrentUserId(); // if (currentUserId != null) { // RemoteUser remoteUser = remoteIdmService.getUser(currentUserId); // if (remoteUser != null) { // userRepresentation = new UserRepresentation(remoteUser); // // if (remoteUser.getGroups() != null && remoteUser.getGroups().size() > 0) { // List<GroupRepresentation> groups = new ArrayList<>(); // for (RemoteGroup remoteGroup : remoteUser.getGroups()) { // groups.add(new GroupRepresentation(remoteGroup)); // } // userRepresentation.setGroups(groups); // } // // if (remoteUser.getPrivileges() != null && remoteUser.getPrivileges().size() > 0) { // userRepresentation.setPrivileges(remoteUser.getPrivileges()); // } // // } // } UserRepresentation userRepresentation = new UserRepresentation(); userRepresentation.setFirstName("admin"); userRepresentation.setLastName("admin"); userRepresentation.setFullName("admin"); userRepresentation.setId("admin"); List<String> pris = new ArrayList<>(); pris.add(DefaultPrivileges.ACCESS_MODELER); pris.add(DefaultPrivileges.ACCESS_IDM); pris.add(DefaultPrivileges.ACCESS_ADMIN); pris.add(DefaultPrivileges.ACCESS_TASK); pris.add(DefaultPrivileges.ACCESS_REST_API); userRepresentation.setPrivileges(pris); if (userRepresentation != null) { return userRepresentation; } else { throw new NotFoundException(); } } }
5. 修改SecurityUtils类,保证包目录不变。
/* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.flowable.ui.common.security; import org.flowable.idm.api.User; import org.flowable.ui.common.model.RemoteUser; import org.flowable.ui.common.security.DefaultPrivileges; import org.flowable.ui.common.security.FlowableAppUser; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import java.util.ArrayList; import java.util.List; /** * Utility class for Spring Security. */ public class SecurityUtils { private static User assumeUser; private SecurityUtils() { } /** * Get the login of the current user. */ public static String getCurrentUserId() { User user = getCurrentUserObject(); if (user != null) { return user.getId(); } return null; } /** * @return the {@link User} object associated with the current logged in user. */ public static User getCurrentUserObject() { if (assumeUser != null) { return assumeUser; } // User user = null; // FlowableAppUser appUser = getCurrentFlowableAppUser(); // if (appUser != null) { // user = appUser.getUserObject(); // } RemoteUser user = new RemoteUser(); // FlowableAppUser appUser = getCurrentFlowableAppUser(); // if (appUser != null) { // user = appUser.getUserObject(); // } user.setId("admin"); user.setDisplayName("admin"); user.setFirstName("admin"); user.setLastName("admin"); user.setEmail("admin@admin.com"); user.setPassword("test"); List<String> pris = new ArrayList<>(); pris.add(DefaultPrivileges.ACCESS_MODELER); pris.add(DefaultPrivileges.ACCESS_IDM); pris.add(DefaultPrivileges.ACCESS_ADMIN); pris.add(DefaultPrivileges.ACCESS_TASK); pris.add(DefaultPrivileges.ACCESS_REST_API); user.setPrivileges(pris); return user; } public static FlowableAppUser getCurrentFlowableAppUser() { FlowableAppUser user = null; SecurityContext securityContext = SecurityContextHolder.getContext(); if (securityContext != null && securityContext.getAuthentication() != null) { Object principal = securityContext.getAuthentication().getPrincipal(); if (principal instanceof FlowableAppUser) { user = (FlowableAppUser) principal; } } return user; } public static boolean currentUserHasCapability(String capability) { FlowableAppUser user = getCurrentFlowableAppUser(); for (GrantedAuthority grantedAuthority : user.getAuthorities()) { if (capability.equals(grantedAuthority.getAuthority())) { return true; } } return false; } public static void assumeUser(User user) { assumeUser = user; } public static void clearAssumeUser() { assumeUser = null; } }
6. 修改SecurityConfiguration类,保证包目录不变。
/* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.flowable.ui.modeler.conf; import org.flowable.ui.common.properties.FlowableRestAppProperties; import org.flowable.ui.common.security.ActuatorRequestMatcher; import org.flowable.ui.common.security.ClearFlowableCookieLogoutHandler; import org.flowable.ui.common.security.DefaultPrivileges; import org.flowable.ui.modeler.properties.FlowableModelerAppProperties; import org.flowable.ui.modeler.security.AjaxLogoutSuccessHandler; import org.flowable.ui.modeler.security.RemoteIdmAuthenticationProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.info.InfoEndpoint; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter; /** * Based on http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#multiple-httpsecurity * * @author Joram Barrez * @author Tijs Rademakers * @author Filip Hrisafov */ @Configuration @EnableWebSecurity public class SecurityConfiguration { private static final Logger LOGGER = LoggerFactory.getLogger(org.flowable.ui.modeler.conf.SecurityConfiguration.class); public static final String REST_ENDPOINTS_PREFIX = "/app/rest"; @Autowired protected RemoteIdmAuthenticationProvider authenticationProvider; // @Bean // public FlowableCookieFilterRegistrationBean flowableCookieFilterRegistrationBean(RemoteIdmService remoteIdmService, FlowableCommonAppProperties properties) { // FlowableCookieFilterRegistrationBean filter = new FlowableCookieFilterRegistrationBean(remoteIdmService, properties); // filter.addUrlPatterns("/app/*"); // filter.setRequiredPrivileges(Collections.singletonList(DefaultPrivileges.ACCESS_MODELER)); // return filter; // } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) { // Default auth (database backed) try { auth.authenticationProvider(authenticationProvider); } catch (Exception e) { LOGGER.error("Could not configure authentication mechanism:", e); } } @Configuration @Order(10) public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { // @Autowired // protected FlowableCookieFilterRegistrationBean flowableCookieFilterRegistrationBean; @Autowired protected AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler; @Override protected void configure(HttpSecurity http) throws Exception { http .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() // .addFilterBefore(flowableCookieFilterRegistrationBean.getFilter(), UsernamePasswordAuthenticationFilter.class) .logout() .logoutUrl("/app/logout") .logoutSuccessHandler(ajaxLogoutSuccessHandler) .addLogoutHandler(new ClearFlowableCookieLogoutHandler()) .and() .csrf() .disable() // Disabled, cause enabling it will cause sessions .headers() .frameOptions() .sameOrigin() .addHeaderWriter(new XXssProtectionHeaderWriter()) .and() .authorizeRequests() // .antMatchers(REST_ENDPOINTS_PREFIX + "/**").hasAuthority(DefaultPrivileges.ACCESS_MODELER); .antMatchers(REST_ENDPOINTS_PREFIX + "/**").permitAll(); } } // // BASIC AUTH // @Configuration @Order(1) public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { protected final FlowableRestAppProperties restAppProperties; protected final FlowableModelerAppProperties modelerAppProperties; public ApiWebSecurityConfigurationAdapter(FlowableRestAppProperties restAppProperties, FlowableModelerAppProperties modelerAppProperties) { this.restAppProperties = restAppProperties; this.modelerAppProperties = modelerAppProperties; } protected void configure(HttpSecurity http) throws Exception { http .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .csrf() .disable(); http.antMatcher("/api/**").authorizeRequests().antMatchers("/api/**").permitAll(); // if (modelerAppProperties.isRestEnabled()) { // // if (restAppProperties.isVerifyRestApiPrivilege()) { // http.antMatcher("/api/**").authorizeRequests().antMatchers("/api/**").hasAuthority(DefaultPrivileges.ACCESS_REST_API).and().httpBasic(); // } else { // http.antMatcher("/api/**").authorizeRequests().antMatchers("/api/**").authenticated().and().httpBasic(); // // } // // } else { // http.antMatcher("/api/**").authorizeRequests().antMatchers("/api/**").denyAll(); // // } } } // // Actuator // @ConditionalOnClass(EndpointRequest.class) @Configuration @Order(5) // Actuator configuration should kick in before the Form Login there should always be http basic for the endpoints public static class ActuatorWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { http .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .csrf() .disable(); http .requestMatcher(new ActuatorRequestMatcher()) .authorizeRequests() .requestMatchers(EndpointRequest.to(InfoEndpoint.class, HealthEndpoint.class)).authenticated() .requestMatchers(EndpointRequest.toAnyEndpoint()).hasAnyAuthority(DefaultPrivileges.ACCESS_ADMIN) .and().httpBasic(); } } }
7. 配置日志信息,flowable使用的是slf4j日志,新建文件log4j.properties,内容如下:
log4j.rootLogger=DEBUG, CA log4j.appender.CA=org.apache.log4j.ConsoleAppender log4j.appender.CA.layout=org.apache.log4j.PatternLayout log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
8. 项目配置,新建application.yml,文件内容:
################### 端口配置 ################### server: port: 8989 ################### spring配置 ################### spring: datasource: url: jdbc:mysql://localhost:3306/flowable?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&nullCatalogMeansCurrent=true username: root password: root driver-class-name: com.mysql.jdbc.Driver flowable: common: app: idm-url: http://localhost:8989/flowable-idm idm-admin: user: admin password: test async-executor-activate: false #关闭定时任务JOB database-schema-update: false
9. 启动项目,访问http://localhost:8989/即可