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">
<parent>
<artifactId>t_qiangqiang_crm</artifactId>
<groupId>com.qiangqiang</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>activiti_spring</artifactId>
<properties>
<java.version>1.8</java.version>
<activiti.version>7.0.0.Beta1</activiti.version>
</properties>
<dependencies>
<!--activiti的核心包-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${activiti.version}</version>
</dependency>
<!--activiti的spring整合包-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>${activiti.version}</version>
</dependency>
<!--activiti的模型处理包-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>${activiti.version}</version>
</dependency>
<!--activiti的bpmn转换包-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!--activiti的json转换包-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!--activiti的bpmn布局包-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-layout</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.activiti.cloud/activiti-cloud-services-api -->
<!--activiti的云支持-->
<dependency>
<groupId>org.activiti.cloud</groupId>
<artifactId>activiti-cloud-services-api</artifactId>
<version>${activiti.version}</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
<scope>test</scope>
</dependency>
<!--单测依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>
</project>
application.properties
与springboot整合中,流程定义文件是可以自动检测生成流程定义的,在于配置文件中的spring.activiti.check-process-definitions属性,true就是自动检测,会自动检测resource文件夹下的processes文件夹下的bpmn文件
server.port=8000
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#<!--false:默认值,activiti启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常(生产环境常用)-->
#<!--true:activiti会对数据库中所有表进行更新操作,如果表不存在,则自动创建-->
#<!--create_drop:在activiti启动时创建表,在关闭时删除(必须手动关闭引擎,才能删除表),单元测试常用-->
#<!--drop_create:在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)-->
spring.activiti.database-schema-update=true
#记录历史等级,可配置的历史级别有none,activiti,audit,full
#none:不保存任何的历史数据,因此,在流程实例与流程行为,这是最高效的
#activiti:级别高于none,保存流程实例与流程行为,其他数据不保存
#audit:除了activiti级别会保存的数据外,还会保存全部的流程任务极其属性,audit为history的默认值
#full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等
spring.activiti.db-history-used=true
#校验流程文件,默认校验resources下的processes文件夹里的流程文件,true为校验,false不校验
spring.activiti.check-process-definitions=true
#druid连接池
spring.datasource.druid.initialSize=5
spring.datasource.druid.minIdle=5
spring.datasource.druid.maxActive=30
spring.datasource.druid.WebStatFilter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*
spring.datasource.druid.remove-abandoned=true
spring.datasource.druid.remove-abandoned-timeout=180
spring.datasource.druid.log-abandoned=true
activiti7与springboot整合后,默认是集成了spring-security的,因此需要对spring-security进行配置,当然配置的内容在activiti7的官网也可以找到模板
这里是内存用户管理InMemoryUserDetailsManager
UserDetailsService的实现类还有一个JdbcUserDetailsManager
也有同样的createUser方法,使用的方式差别不大,可以从数据库中查询用户
package com.qiangqiang.config;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
/**
* \* Created with IntelliJ IDEA.
* \* @author: xiyue
* \* Date: 2021/1/6
* \* Time: 11:29
* \* To change this template use File | Settings | File Templates.
* \* Description:
* \ security的配置类
*/
@Slf4j
@Configuration
public class DemoApplicationConfiguration {
private Logger logger = LoggerFactory.getLogger(DemoApplicationConfiguration.class);
@Bean()
public UserDetailsService myUserDetailsService(){
//内存用户管理
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
String [] [] usersGroupsAndRoles = {
{"zhangsan", "password", "ROLE_ACTIVITI_USER"},
{"jerry", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"jack", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"rose", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"},
{"system", "password", "ROLE_ACTIVITI_USER"},
{"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;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
SecurityUtil
这个工具类的意义在于logInAs方法,在activiti7对流程进行一系列操作的时候,会有授权,不管是部署流程定义,创建流程实例,启动流程实例,查询流程实例,完成个人任务,拾取任务,都需要授权
不然会报错:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
这个错误就是典型的权限不足
package com.qiangqiang.util;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
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;
/**
* \* Created with IntelliJ IDEA.
* \* @author: xiyue
* \* Date: 2021/1/6
* \* Time: 11:00
* \* To change this template use File | Settings | File Templates.
* \* Description:
* \ activiti与springboot整合之后,默认集成了spring security
*/
@Component
public class SecurityUtil {
@Autowired
@Qualifier("myUserDetailsService")
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 b) throws IllegalArgumentException {
}
@Override
public String getName() {
return user.getUsername();
}
}
)
);
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
}
}
用到的User对象
package com.qiangqiang.entity;
import java.util.Date;
public class User {
private Integer id;
private String name;
private String loginname;
private Integer sex;
private String remark;
private String pwd;
private String salt;
public User(Integer id, String name, String loginname, Integer sex, String remark, String pwd, String salt) {
this.id = id;
this.name = name;
this.loginname = loginname;
this.sex = sex;
this.remark = remark;
this.pwd = pwd;
this.salt = salt;
}
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLoginname() {
return loginname;
}
public void setLoginname(String loginname) {
this.loginname = loginname;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
}
测试类,基本上可以完成操作的需求
package com.qiangqiang;
import com.qiangqiang.config.DemoApplicationConfiguration;
import com.qiangqiang.util.SecurityUtil;
import lombok.extern.slf4j.Slf4j;
import org.activiti.api.process.model.ProcessDefinition;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.CompleteTaskPayloadBuilder;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.model.payloads.CompleteTaskPayload;
import org.activiti.api.task.runtime.TaskRuntime;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestMapping;
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class ActivitiApplicationTests {
private Logger logger = LoggerFactory.getLogger(DemoApplicationConfiguration.class);
@Autowired
public ProcessRuntime processRuntime;
@Autowired
public TaskRuntime taskRuntime;
@Autowired
public SecurityUtil securityUtil;
@Test
public void findProcess() {
//执行测试方法的时候要定义下谁在执行这个方法,,给权限,不然报错
//An Authentication object was not found in the SecurityContext
securityUtil.logInAs("zhangsan");
//查询所有流程定义信息
Page<ProcessDefinition> processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 10));
System.out.println("当前流程定义的数量:"+processDefinitionPage.getTotalItems());
//获取流程信息
for (ProcessDefinition processDefinition:processDefinitionPage.getContent()) {
System.out.println("---------------------------------");
System.out.println("流程定义信息"+processDefinition);
}
}
//测试流程启动
@Test
public void startProcess(){
//执行测试方法的时候要定义下谁在执行这个方法,,给权限,不然报错
//An Authentication object was not found in the SecurityContext
securityUtil.logInAs("zhangsan");
ProcessInstance instance = processRuntime.start(ProcessPayloadBuilder.start().withProcessDefinitionKey("myEvection").build());
logger.info("流程实例启动了:" + instance);
}
/**
* 查询流程定义
*/
@Test
public void getProcess(){
// securityUtil.logInAs("zhangsan");
//查询所有流程定义信息
Page<ProcessDefinition> processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 10));
System.out.println("当前流程定义的数量:"+processDefinitionPage.getTotalItems());
//获取流程信息
for (ProcessDefinition processDefinition:processDefinitionPage.getContent()) {
System.out.println("流程定义信息"+processDefinition);
}
}
//完成流程任务
//拾取任务
@Test
public void getTask(){
//执行测试方法的时候要定义下谁在执行这个方法,,给权限,不然报错
//An Authentication object was not found in the SecurityContext
securityUtil.logInAs("zhangsan");
//查询任务
Page<Task> tasks = taskRuntime.tasks(Pageable.of(0, 10));
System.out.println("这里是产村出来tasks的个数"+ tasks.getTotalItems());
for (Task task : tasks.getContent()) {
//拾取任务
//使用的TaskPayloadBuilder来构建一个TaskPayload对象
taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
System.out.println("拾取任务:"+task.getName());
//执行任务
taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
logger.info("完成任务:"+task.getName());
}
}
}
其中,ProcessRuntime和TaskRuntime为整合springboot后activiti7新增的service,其方法思想和原来的4个service是差不多的,使用方法就看参数需要什么就传什么就行