Activiti 7 如何接入自己系统的身份管理

一、 官方示例

首先,官网给出了简单了demo 示例:https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example/src/main/java/org/activiti/examples/DemoApplicationConfiguration.java#L26

建议大家将完成的demo下载下来仔细看下。下面说下其主要思路

 

1 SecurityUtil

@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);
    }
}

 

此类关键代码 一

 SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
            @Override
            public Collection<? extends GrantedAuthority> getAuthorities() {
                return user.getAuthorities();

认证上下文中设置了Authentication 对象,此对象返回了认证者的权限Authorities集合,后面会拿这个结果做判定,如果没有 ROLE_ACTIVITI_USER 将会抛出异常,提示无法访问。

此类关键代码 二

org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);

  往 Authentication 上下文中设置了登录者,注意了,此处的Authentication 和前面Spring SecurityContextHolder 中的不一样,二者包名不一样。activiti 中 Authentication中的线程变量用于后面的用户信息获取。比如设置发起人,当前用户的任务等。

2 配置类

@Bean
public UserDetailsService myUserDetailsService() {


    InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();


    String[][] usersGroupsAndRoles = {
            {"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

注入了 InMemoryUserDetailsManager 这个用户服务,新建了两个用户 system ,admin,具备相应的角色,请注意 ROLE_ACTIVITI_ADMIN 的用户将无法访问调用相关的api 。

 

二、 接入自定义身份

 

根据以上两点,我们就可以改造,并接入自己的身份系统。

 

1 用户

1 编写 ActivitiUserDetailsManager

UserDetailsService 将会注入我们自己的身份服务ActivitiUserDetailsManager。其实现我们仿造InMemoryUserDetailsManager 类即可,关键代码是重写 如下方法:

@Override
    public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
        BizUserEntity bizUser = userService.getBizUserById(userId);
        return new User(bizUser.getName(), "", Collections.singletonList(new SimpleGrantedAuthority("ROLE_ACTIVITI_USER")));
    }

其中

BizUserEntity bizUser = userService.getBizUserById(userId);

这个是自己业务中身份服务查询方法,并且将此对象转换为如下用户

org.springframework.security.core.userdetails.User;

并且具备ROLE_ACTIVITI_USER 这个角色。

 

2 配置类

和demo一样,注入UserDetailsService 即可。

@Bean
public UserDetailsService activitiUserDetailsService() {
    return new ActivitiUserDetailsManager(userService,groupManager);
}

   此次的参数 userService,groupManager 就是自己业务系统的用户/组管理服务,我这里是通过构造器传入进去的,你也可以通过其他方式实现注入。说白了就是ActivitiUserDetailsManager 这个类呢对你自己的userService 又作了一层简单的包装,这样可以做到和工作流的用户服务解耦。

2 用户组

 等等,以上的案例只是说明了用户查询,但是用户组呢?其实activiti 7 中,有默认的实现

org.activiti.core.common.spring.identity.ActivitiUserGroupManagerImpl

其中,有两个最重要的方法:获取用户组,获取用户角色

public List<String> getUserGroups(String username) {
    return (List)this.userDetailsService.loadUserByUsername(username).getAuthorities().stream().filter((a) -> {
        return a.getAuthority().startsWith("GROUP_");
    }).map((a) -> {
        return a.getAuthority().substring(6);
    }).collect(Collectors.toList());
}


public List<String> getUserRoles(String username) {
    return (List)this.userDetailsService.loadUserByUsername(username).getAuthorities().stream().filter((a) -> {
        return a.getAuthority().startsWith("ROLE_");
    }).map((a) -> {
        return a.getAuthority().substring(5);
    }).collect(Collectors.toList());
}

这里其实也是通过userDetailsService 这个用户查询来做的,并且 是通过前缀匹配去查询 ROLE_ , GROUP_ 的字符串,到这里也就不难理解demo中如下代码了

{"system", "password", "ROLE_ACTIVITI_USER"},
{"admin", "password", "ROLE_ACTIVITI_ADMIN"}

因此,仿造ActivitiUserGroupManagerImpl 的实现,新建 ActivitiGroupManagerImpl 实现

org.activiti.api.runtime.shared.identity.UserGroupManager

重写相关的方法即可。值得注意的一点是,需要加上@Primary这个注解

 

 因为 默认的 ActivitiUserGroupManagerImpl 也是自动交给spring初始化了,加上@Primary 就是告诉spring 当有多个 实现类shi,用ActivitiGroupManagerImpl 这个类。

到此,就完全接入自己的身份系统了,包含用户和用户组的能力。对于 activiti7 以下的版本,实现会有很大的差异,但是官方文档给出了解决方案,参见如下:

https://www.activiti.org/userguide/index.html#advanced.custom.session.manager

 

https://my.oschina.net/woniuyi/blog/4714756

posted @ 2022-01-14 16:48  wq9  阅读(2197)  评论(0编辑  收藏  举报