前言
初学activiti工作流引擎的时候踩过很多坑,在此进行统一的做个总结。
activiti的依赖中包含springsecurity,需要先进行security的配置(非常重要!!!非常重要!!!非常重要!!!非常重要!!!非常重要!!!非常重要!!!非常重要!!!)
1. 引入activiti依赖
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.0.0.Beta2</version>
</dependency>
2. 相关springboot配置
server:
port: 8088
spring:
datasource:
url: jdbc:mysql://192.168.11.4:3306/activiti?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 1234
# activiti7配置
activiti:
# 自动部署验证设置:true-开启(默认)、false-关闭
check-process-definitions: true
# 保存历史数据
history-level: full
# 检测历史表是否存在
db-history-used: true
# 关闭SpringAutoDeployment
deployment-mode: never-fail
# 对数据库中所有表进行更新操作,如果表不存在,则自动创建
database-schema-update: true
# 解决频繁查询SQL问题
async-executor-activate: false
!!!!!!!注意
activiti会扫描数据库中act开头的表,如果有则不会初始化创建新表,database-schema-update失效,将数据源配置中的url中增加参数 nullCatalogMeansCurrent=true 即可解决 !!!!!!!!!!!!
3. 配置SpringSecurity相关
这里简单的配置一下几个用户放到内存中,正常情况下应该是一个完善的权限配置。这里就不多赘述,有兴趣的话可以去了解SpringSecurtiy的校验流程。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Configuration
@EnableWebSecurity
public class DemoApplicationConfiguration extends WebSecurityConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(DemoApplicationConfiguration.class);
@Override
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService());
}
@Bean
public UserDetailsService myUserDetailsService() {
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
String[][] usersGroupsAndRoles = {
{"salaboy", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"ryandawsonuk", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"erdemedeiros", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"},
{"admin", "password", "ROLE_ACTIVITI_ADMIN"},
};
for (String[] user : usersGroupsAndRoles) {
List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
logger.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]");
inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]),
authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList())));
}
return inMemoryUserDetailsManager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
工具类,用来模拟登录权限认证,主要是将isAuthenticated属性设置为true获取权限。此处不在过多赘述,详情请自行翻阅SpringSecurity的认证流程。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import java.util.Collection;
@Component
public class SecurityUtil {
@Autowired
private UserDetailsService userDetailsService;
public void logInAs(String username) {
UserDetails user = userDetailsService.loadUserByUsername(username);
if (user == null) {
throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user");
}
SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}
@Override
public Object getCredentials() {
return user.getPassword();
}
@Override
public Object getDetails() {
return user;
}
@Override
public Object getPrincipal() {
return user;
}
@Override
public boolean isAuthenticated() {
return true;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
@Override
public String getName() {
return user.getUsername();
}
}));
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
}
}
4. 创建流程图文件
默认路径为resources下的processes,可在springboot配置文件中自行更改。
绘制bpmn流程图需要idea安装插件actiBPM,高版本的idea已经搜不到这个插件,可以去https://plugins.jetbrains.com/ 下载,然后本地安装即可。
重启idea,在processes下新建Bqmn文件
双击打开这个文件
可以看到插件有三个部分,第一个部分就是流程图,第二个部分就是流程图中使用的结点图标,第三部分就是流程图及结点的定义信息。
!!!!!注意,如果打开后缺少第三部分 bpmn editor,重新设置idea主题即可,目前原因未知。
关闭bqmn文件,再次打开bqmn文件,即可看到第三部分 bpmn editor。
!!!!!!注意,如果出现第一部分流程图结点的名称设置成中文之后,关闭bqmn文件之后重新打开乱码这种情况,请设置文件编码为UTF-8。
-Dfile.encoding=utf-8
5. 启动springboot
启动springboot,会发现在第二步配置的数据源中的activiti数据库中已经新增了一批表完成初始化,并且插入了一条流程