ums-core-spring-boot-starter

ums-core feature: username password login, mobile login, Support multi-tenancy, sign, JWT, MDC etc.

License

License

Categories

Categories

Spring Boot Container Microservices
GroupId

GroupId

top.dcenter
ArtifactId

ArtifactId

ums-core-spring-boot-starter
Last Version

Last Version

2.2.27
Release Date

Release Date

Type

Type

jar
Description

Description

ums-core-spring-boot-starter
ums-core feature: username password login, mobile login, Support multi-tenancy, sign, JWT, MDC etc.
Project URL

Project URL

https://github.com/ZeroOrInfinity/UMS
Source Code Management

Source Code Management

https://github.com/ZeroOrInfinity/UMS

Download ums-core-spring-boot-starter

How to add to project

<!-- https://jarcasting.com/artifacts/top.dcenter/ums-core-spring-boot-starter/ -->
<dependency>
    <groupId>top.dcenter</groupId>
    <artifactId>ums-core-spring-boot-starter</artifactId>
    <version>2.2.27</version>
</dependency>
// https://jarcasting.com/artifacts/top.dcenter/ums-core-spring-boot-starter/
implementation 'top.dcenter:ums-core-spring-boot-starter:2.2.27'
// https://jarcasting.com/artifacts/top.dcenter/ums-core-spring-boot-starter/
implementation ("top.dcenter:ums-core-spring-boot-starter:2.2.27")
'top.dcenter:ums-core-spring-boot-starter:jar:2.2.27'
<dependency org="top.dcenter" name="ums-core-spring-boot-starter" rev="2.2.27">
  <artifact name="ums-core-spring-boot-starter" type="jar" />
</dependency>
@Grapes(
@Grab(group='top.dcenter', module='ums-core-spring-boot-starter', version='2.2.27')
)
libraryDependencies += "top.dcenter" % "ums-core-spring-boot-starter" % "2.2.27"
[top.dcenter/ums-core-spring-boot-starter "2.2.27"]

Dependencies

compile (20)

Group / Artifact Type Version
top.dcenter : ums-commons jar 2.2.27
top.dcenter : ums-jwt jar 2.2.27
top.dcenter : ums-mdc jar 2.2.27
top.dcenter : ums-vc jar 2.2.27
org.springframework.boot : spring-boot-starter jar
org.springframework.security : spring-security-core jar
org.springframework.security : spring-security-web jar
org.springframework.security : spring-security-config jar
org.springframework.security : spring-security-oauth2-resource-server jar
org.springframework.boot : spring-boot-configuration-processor jar
org.springframework.boot : spring-boot-starter-validation jar
org.springframework.session : spring-session-core jar
org.springframework.data : spring-data-commons jar
org.springframework.boot : spring-boot-starter-jdbc jar
org.springframework.boot : spring-boot-starter-web jar
org.springframework.boot : spring-boot-starter-aop jar
org.springframework.boot : spring-boot-starter-data-redis jar
org.apache.commons : commons-pool2 jar
mysql : mysql-connector-java jar
com.github.spotbugs : spotbugs-annotations Optional jar 3.1.12

provided (4)

Group / Artifact Type Version
com.google.guava : guava jar 30.0-jre
io.springfox : springfox-boot-starter jar 3.0.0
org.projectlombok : lombok jar 1.18.12
org.projectlombok : lombok-maven-plugin maven-plugin 1.18.12.0

test (1)

Group / Artifact Type Version
org.springframework.boot : spring-boot-devtools jar

Project Modules

There are no modules declared in this project.

User management scaffolding

UMS is a non-intrusive, highly decoupled from business, customizable user management scaffolding

maven license JDK Docs gitee star github star MySQL Redis SpringBoot SpringSecurity JustAuth

User management scaffolding, integration: User password login, mobile login, OAuth2 login(Based on JustAuth), Support multi-tenancy, jwt , validate code(image, sms, sliderCode), RBAC, SLF4J-MDC, signed etc...

ums-arch

一、UMS feature list

  • validate code(image, SMS, slider) verification function.
  • Mobile login function, automatic registration after login, Support multi-tenancy.
  • Support all third-party authorized logins supported by JustAuth, after login, automatically register or bind or create temporary users(TemporaryUser).
    • Support timing refresh accessToken, support distributed timing tasks.
    • Support the caching function of user table and token table by OAuth2 login.
    • Support third-party binding and unbinding and query interfaces.
  • Access control function, support multi tenancy.
  • Simplify session、remember me、csrf cors etc configuration.
  • Return json or html data according to the set response method (JSON and REDIRECT).
  • signed function.
  • Support log link tracking function based on SLF4J MDC mechanism.
  • JWT creation, verification, refresh, concurrent access problems caused by jwt invalid, and blacklist functions.

Module function

Module Function
commons common component module
ums Integrated commons/core/vc/mdc/oauth/rbac/jwt module
core Username password login/Mobile login and automatic registration/signed/Simplify session、remember me、csrf cors etc configuration/session redis cache/Return json or html data according to the set response method (JSON and REDIRECT)/JWT/mdc model
vc validate code(image, SMS, slider) verification function, integrated mdc model
mdc Support log link tracking function based on SLF4J MDC mechanism
oauth OAuth2 login by JustAuth, integrated jwt/mdc model
rbac RBAC-based access control, supports multi-tenancy, integrated mdc model
jwt JWT function, integrated mdc model
dependencies UMS Dependencies
demo basic-example/basic-detail-example/permission-example/quickStart/session-detail-example/validate-codi-example/justAuth-security-oauth2-example/tenant-example/jwt-example

demo

demo demo function
basic-example Basic function: the simplest configuration
basic-detail-example Detailed configuration of basic functions: anonymous/session simple configuration/rememberMe/csrf/cors/login routing/signed
permission-example RBAC-based permission function settings
quickStart quick start example
multi-tenancy-example multi tenant registration and login example
justAuth-security-oauth2-example Detailed example of third-party authorized login, MDC log link tracking configuration
session-detail-example Session and session cache detailed configuration
validate-code-example Basic functions: verification code (including slider verification code), mobile login configuration
jwt-example JWT function example

CN doc EN doc

CN doc EN doc

微信群:UMS 添加微信(z56133)备注(UMS)

weixin

二、maven

<dependency>
    <groupId>top.dcenter</groupId>
    <artifactId>ums-spring-boot-starter</artifactId>
    <version>[2.2.0,)</version>
</dependency>

三、TODO List:

  • 1. microservices, OAuth2 authenticate server

四、Quick Start


五、Interface instructions:

The interface that needs to be implemented when the corresponding function is present:

  1. user service: Must implemente

  2. RBAC-based access control: Support multi-tenancy

    • UriAuthorizeService: 推荐通过实现 AbstractUriAuthorizeService 来实现此接口

    • AbstractUriAuthorizeService: 必须实现(Must implemente)

      • uri(资源) 访问权限控制服务接口抽象类, 定义了基于(角色/多租户/SCOPE)的访问权限控制逻辑. 实现 AbstractUriAuthorizeService 抽象类并注入 IOC 容器即可替换 DefaultUriAuthorizeService.

      • 注意:

        1. 推荐实现 AbstractUriAuthorizeService 同时实现 UpdateCacheOfRolesResourcesService 更新与缓存权限服务, 有助于提高授权服务性能.

        2. 对传入的 Authentication 的 authorities 硬性要求:

         // 此 authorities 可以包含:  [ROLE_A, ROLE_B, ROLE_xxx TENANT_110110, SCOPE_read, SCOPE_write, SCOPE_xxx]
         // authorities 要求:
         //    1. 角色数量    >= 0
         //    2. SCOPE 数量 >= 0
         //    3. 多租户数量  1 或 0
         //    4. 角色数量 + SCOPE 数量  >= 1
         Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

        3. 此框架默认实现 hasPermission(Authentication, HttpServletRequest) 方法访问权限控制, 通过 UriAuthoritiesPermissionEvaluator 实现, 使用此接口的前提条件是: 应用使用的是 restful 风格的 API; 如果不是 restful 风格的 API, 请使用 hasPermission(Authentication, String, String) 接口的访问权限控制, 此接口使用注解的方式 @PerAuthorize("hasPermission('/users', 'list')") 来实现, 使用注解需先开启 @EnableGlobalMethodSecurity(prePostEnabled = true) 注解.

    • UpdateCacheOfRolesResourcesService:

      • 用于更新并缓存基于(角色/多租户/SCOPE)角色的权限的服务接口, 每次更新角色的 uri(资源)权限时,需要调用此接口, 推荐实现此 RolePermissionsService 接口, 会自动通过 AOP 方式实现发布 UpdateRolesResourcesEvent 事件, 从而调用 UpdateCacheOfRolesResourcesService 对应的方法.

      • 建议:

        1. 基于 角色 的权限控制: 实现所有角色 uri(资源) 的权限 Map(role, map(uri, Set(permission))) 的更新与缓存本机内存.

        2. 基于 SCOPE 的权限控制: 情况复杂一点, 但 SCOPE 类型比较少, 也还可以像 1 的方式实现缓存本机内存与更新.

        3. 基于 多租户 的权限控制: 情况比较复杂, 租户很少的情况下, 也还可以全部缓存在本机内存, 通常情况下全部缓存内存不现实, 只能借助于类似 redis 等的内存缓存.

    • RolePermissionsService:

      • 更新与查询基于(角色/多租户/SCOPE)的角色资源服务接口. 主要用于给角色添加权限的操作.

      • 注意:

        1. 在添加资源时, 通过PermissionType.getPermission() 来规范的权限格式, 因为要支持 restful 风格的 Api, 在授权时需要对 HttpMethod 与对应的权限进行匹配判断

        2. 如果实现了 UpdateCacheOfRolesResourcesService 接口, 未实现 RolePermissionsService 接口, 修改或添加基于"角色/多租户/SCOPE "的资源权限时一定要调用 UpdateCacheOfRolesResourcesService 对应的方法, 有两种方式: 一种发布事件, 另一种是直接调用对应服务;

        // 1. 推荐用发布事件(异步执行)
        applicationContext.publishEvent(new UpdateRolesResourcesEvent(true, UpdateRoleResourcesDto);
        // 2. 直接调用服务
        // 角色权限资源
        UpdateCacheOfRolesResourcesService.updateAuthoritiesByRoleId(roleId, resourceClass, resourceIds);
        // 多租户的角色权限资源
        UpdateCacheOfRolesResourcesService.updateAuthoritiesByRoleIdOfTenant(tenantId, roleId, resourceClass, resourceIds);
        // SCOPE 的角色权限资源
        UpdateCacheOfRolesResourcesService.updateAuthoritiesByScopeId(scopeId, roleId, resourceClass, resourceIds);
        // 角色组权限资源
        UpdateCacheOfRolesResourcesService.updateRolesByGroupId(groupId, roleIds);
        // 多租户的角色组权限资源
        UpdateCacheOfRolesResourcesService.updateRolesByGroupIdOfTenant(tenantId, groupId, roleIds);

        3. 实现此 RolePermissionsService 接口, 不需要执行上两种方法的操作, 已通过 AOP 方式实现发布 UpdateRolesResourcesEvent 事件.

        4. 注意: RolePermissionsServiceAspect 切面生效前提, 事务的 Order 的值必须 大于 1, 如果是默认事务(优先级为 Integer.MAX_VALUE )不必关心这个值, 如果是自定义事务, 且设置了 Order 的值, 那么值必须 大于 1.

    • 时序图

时序图

  1. 短信验证码(SMS validate code): 默认空实现

  2. 图片验证码(image validate code): 已实现缓存功能, 支持定时刷新缓存功能, 可以自定义缓存验证码图片的输出路径与缓存数量

  3. 滑块验证码(Slider validate code): 已实现缓存功能, 支持定时刷新缓存功能, 可以自定义缓存验证码图片的输出路径与缓存数量, 支持自定义源图片路径与模板图片路径(源图片与模板图片参考validate-code-example)

  4. 自定义验证码(customize validate code):

  5. Auth2StateCoder: 用户需要时实现, 对第三方授权登录流程中的 state 进行自定义编解码. 可以传递必要的信息, 如: 第三方登录成功的跳转地址等 注意此接口的两个方法必须同时实现对应的编解码逻辑, 实现此接口后注入 IOC 容器即可, 如有前端向后端获取 authorizeUrl 时向后端传递额外参数 且用作注册时的信息, 需配合 UmsUserDetailsService.registerUser(AuthUser, String, String, String) 方法实现.

  6. Auth2UserService: 获取第三方用户信息的接口, 一般不需要用户实现, 除非想自定义获取第三方用户信息的逻辑, 实现此接口注入 IOC 容器即可替代.

  7. UsersConnectionRepository: 第三方授权登录的第三方用户信息增删改查, 绑定与解绑及查询是否绑定与解绑接口, 一般不需要用户实现. 除非想自定义获取第三方用户信息的逻辑, 实现此接口注入 IOC 容器即可替代.

  8. UsersConnectionTokenRepository: 第三方授权登录用户 accessToken 信息表增删改查接口, 一般不需要用户实现. 除非想自定义获取第三方用户信息的逻辑, 实现此接口注入 IOC 容器即可替代.

  9. ConnectionService: 第三方授权登录用户的注册, 绑定, 更新第三方用户信息与 accessToken 信息的接口, 一般不需要用户实现. 除非想自定义获取第三方用户信息的逻辑, 实现此接口注入 IOC 容器即可替代.

    • 注意: 要替换内置 auth_tokenuser_connection 表的实现逻辑, 实现此接口注入 IOC 容器即可, 且设置属性 ums.repository. enableStartUpInitializeTable = false .
  10. 自定义 OAuth2 Login 扩展接口: 内置两个自定义 providerId(ums.oauth.customize 与 ums.oauth.gitlabPrivate)

    • AuthGitlabPrivateSource: 抽象类, 实现此自定义的 AuthGitlabPrivateSource 且注入 ioc 容器的同时, 必须实现 AuthCustomizeRequest , 会自动集成进 OAuth2 Login 逻辑流程中, 只需要像 JustAuth 默认实现的第三方登录一样, 配置相应的属性(ums.oauth.gitlabPrivate.[clientId|clientSecret]等属性)即可.

    • AuthCustomizeSource: 抽象类, 实现此自定义的 AuthCustomizeSource 且注入 ioc 容器的同时, 必须实现 AuthCustomizeRequest , 会自动集成进 OAuth2 Login 逻辑流程中, 只需要像 JustAuth 默认实现的第三方登录一样, 配置相应的属性(ums.oauth.customize.[clientId|clientSecret]等属性)即可.

    • AuthCustomizeRequest: 抽象类, 实现此自定义的 AuthCustomizeRequest 同时, 必须实现 AuthCustomizeSource 或 AuthGitlabPrivateSource 且注入 ioc 容器, 会自动集成进 OAuth2 Login 逻辑流程中, 只需要像 JustAuth 默认实现的第三方登录一样, 配置相应的属性(ums.oauth.customize.[clientId|clientSecret]等属性)即可.

  11. 自定义前后端认证 token 以及通信方式

  12. 如果应用为多租户系统, 且使用 RememberMe 功能: 默认的 RememberMeServices 不支持多租户, 则需要自定义实现 org.springframework.security.web .authentication.RememberMeServices 接口, 使其支持多租户系统, 不过一般情况下多租户系统不使用 RememberMe 功能.

  13. TenantContextHolder:

    • 多租户上下文存储器. 实现此接口并注入 IOC 容器后, 会自动注入 UMS 默认实现的注册/登录/授权组件, 要实现 ums 框架具有多租户功能, 必须实现此接口并注入 IOC 容器.

    • 功能:

      1. tenantIdHandle(HttpServletRequest, String)从注册用户入口或登录用户入口提取 tenantId 及进行必要的逻辑处理(如: tenantId 存入 ThreadLocal , 或存入 session, 或存入 redis 缓存等).

      2. getTenantId()方便后续用户注册、登录、授权的数据处理(如:sql 添加 tenantId 的条件,注册用户添加 TENANT_tenantId 权限,根据 tenantId 获取角色的权限数据).

      3. getTenantId(Authentication) 默认实现方法, 用户已登录的情况下, 获取租户 ID, 直接从 authority 中解析获取.

    • 注意: 1. 多租户系统中, 在未登录时需要用到 tenantId 的接口, 如: UserCache.getUserFromCache(String)/UmsUserDetailsService 等接口, 可通过 getTenantId() 来获取 tenantId. 登录用户可以通过 Authentication 来获取 tenantId.

      2. UMS 默认的登录与注册逻辑中, 都内置了 TenantContextHolder.tenantIdHandle(HttpServletRequest, String) 逻辑, 用户在实现 UserCache/UmsUserDetailsService 等接口中需要 tenantId 时, 调用 TenantContextHolder.getTenantId() 方法即可.

      3. 如果自定义的注册或登录逻辑, 需要自己先调用 TenantContextHolder.tenantIdHandle(HttpServletRequest, String) 逻辑, 再在 实现 UserCache/UmsUserDetailsService 等接口中需要 tenantId 时, 调用 TenantContextHolder.getTenantId() 方法即可.

  14. JobHandler

    • 任务处理器接口, 继承此接口并注入 IOC 容器, top.dcenter.ums.security.core.tasks.config.ScheduleAutoConfiguration 会自动注册到 ScheduledTaskRegistrar 中.
  15. JWT 接口请看 jwt-example.


六、Configurations:

功能(Features) 模块(model) demo模块--简单配置(Simple Configuration) demo模块--详细配置(detail Configuration)
1. 基本功能 core basic-example
2. 登录路由功能 core basic-detail-example
3. session core session-detail-example
4. remember-me core basic-detail-example
5. csrf core basic-detail-example
6. anonymous core basic-detail-example
7. 验证码 core validate-code-example
8. 手机登录 core basic-detail-example
9. 第三方登录 core basic-detail-example
10. 给第三方登录时用的数据库表 user_connection 与 auth_token 添加 redis cache core basic-detail-example
11. 签到 core basic-detail-example
12. 基于 RBAC 的访问权限控制功能 core permission-example
13. 线程池配置 core justAuth-security-oauth2-example
14. 基于 SLF4J MDC 机制的日志链路追踪配置 core justAuth-security-oauth2-example

七、注意事项(NOTE):

1. HttpSecurity 配置问题:UMS 中的 HttpSecurityAware 配置与应用中的 HttpSecurity 配置冲突问题:

  1. 如果是新建应用添加 HttpSecurity 配置, 通过下面的接口即可:

  2. 如果是已存在的应用:

    • 添加 HttpSecurity 配置, 通过下面的接口即可: HttpSecurityAware
    • 已有的 HttpSecurity 配置, 让原有的 HttpSecurity 配置实现此接口进行配置: top.dcenter.security.core.api.config.HttpSecurityAware
  3. 与 spring cloud: 2020.0.0 和 spring 2.4.x 集成时, 因配置文件的加载方式发送变化, 当使用 spring.factories 加载此类时, 会有如下错误提示: Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one .

    • 解决方案:
   // 第一种方案: 使用 spring.factories 加载此类, 再添加下面空的 WebSecurityConfigurerAdapter 配置类,
   // 阻止 spring 自动加载方式默认的 WebSecurityConfigurerAdapter 配置.
   // 适合引入了  top.dcenter:ums-core-spring-boot-starter 或  top.dcenter:ums-spring-boot-starter 模块
   @Configuration
   public class WebSecurityAutoConfigurer extends WebSecurityConfigurerAdapter { }

   // 第二种方案: 不使用 spring.factories 加载此类, 直接注册此类到 IOC 容器.
   // 适合所有模块.
   @Configuration
   public class WebSecurityAutoConfigurer {
       @Bean
       public SecurityCoreAutoConfigurer securityCoreAutoConfigurer() {
           return new SecurityCoreAutoConfigurer();
       }
   }

核心配置逻辑

2. 在 ServletContext 中存储的属性:

  • 属性名称: SecurityConstants.SERVLET_CONTEXT_PERMIT_ALL_SET_KEY
  • 属性值: Set, 把权限类型为 PERMIT_ALL 的 Set 存储在 servletContext .

4. 验证码优先级(Verification code Priority):

  • 同一个 uri 由多种验证码同时配置, 优先级如下: SMS > CUSTOMIZE > SELECTION > TRACK > SLIDER > IMAGE

5. Jackson 序列化与反序列化

// 示例
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
// Auth2Jackson2Module 为此项目实现的反序列化配置     
objectMapper.registerModules(new CoreJackson2Module(), new WebJackson2Module(), new Auth2Jackson2Module());
jackson2JsonRedisSerializer.setObjectMapper(om);
  • 注意: UmsUserDetailsService 的注册用户方法返回的 UserDetails 的默认实现 User 已实现反序列化器, 如果是开发者自定义的子类, 需开发者自己实现反序列化器.

八、Properties Configurations

属性配置列表(Properties Configurations)
基本属性(Basic Properties)
签到属性(Sign Properties)
手机登录属性(Mobile login Properties)
验证码属性(Validate Code Properties)
第三方授权登录(OAuth2 JustAuth)
线程池属性(ThreadPool Properties)
基于 SLF4J MDC 机制的日志链路追踪属性
第三方授权登录用户信息数据 redis 缓存配置(UserConnection Redis cache Properties)
第三方授权登录用户信息表 user_connection sql 配置(UserConnection sql Properties)

九、参与贡献(Participate in contribution)

  1. Fork 本项目
  2. 新建 Feat_xxx 分支
  3. 提交代码
  4. 新建 Pull Request

十、Flow chart: 随着版本迭代会有出入

Login Flow chart

登录流程

JWT Flow chart

纯jwt流程图 jwt-session流程图

Slider verification code Flow chart

sliderValidateCode


十一、时序图(Sequence Diagram): 随着版本迭代会有出入

时序图
csrf
获取验证码逻辑
图片验证码逻辑
logout
第三方授权登录
rememberMe
核心配置逻辑
登录路由
session
手机登录
授权逻辑时序图
过时:第三方绑定与解绑
过时:第三方授权登录
过时:第三方授权登录注册

十二、基于 SLF4J MDC 机制的日志链路追踪功能

  • 使用此功能在日志配置文件中的 pattern 中添加 %X{MDC_TRACE_ID} 即可.
<!-- 控制台 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- 日志格式 -->
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level ${PID:- } --- [%thread] %X{MDC_TRACE_ID} %logger[%L] - %msg%n</pattern>
        <charset>utf-8</charset>
    </encoder>
    <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <!-- 只有这个日志权限才能看,sql语句 -->
        <level>DEBUG</level>
    </filter>
</appender>
  • 自定义 SLF4J MDC 机制实现日志链路追踪 id 的类型: 可通过属性 ums.mdc.type 定义:
# 基于 SLF4J MDC 机制实现日志链路追踪 id 的类型, 默认为 uuid. 当需要自定义 id 时, type = MdcIdType.CUSTOMIZE_ID, 再实现 MdcIdGenerator.getMdcId() 方法, 注入 IOC 容器即可.
ums:
  mdc:
    type: UUID/THREAD_ID/SESSION_ID/CUSTOMIZE_ID

当 ums.mdc.type = CUSTOMIZE_ID 时 需要实现接口 MdcIdGeneratorMdcIdGenerator 并注入 IOC 容器.

  • 多线程使用问题: 父线程新建子线程之前调用 MDC.getCopyOfContextMap() 方法获取 MDC context, 子线程在执行操作前先调用 MDC.setContextMap(context) 方法将父线程的 MDC context 设置到子线程中. ThreadPoolTaskExecutor 的配置请参考 ScheduleAutoConfiguration.
  • 多线程传递 MDC context 简单示例:
final Logger log = LoggerFactory.getLogger(this.getClass());
// 获取父线程 MDC 中的内容
final Map<String, String> context = MDC.getCopyOfContextMap();
final Runnable r = () -> {
    log.info("testMDC");
    System.out.println("...");
};
new Thread(() -> {
    // 将父线程的 MDC context 设置到子线程中
    MDC.setContextMap(context);
    r.run();
}, "testMDC").start();

友情链接

鸣谢

感谢 JetBrains 提供的免费 IntelliJ IDEA Ultimate:

JetBrains

Versions

Version
2.2.27
2.2.26
2.2.25
2.2.24
2.2.23
2.2.22
2.2.21
2.2.20
2.2.19
2.2.18
2.2.17
2.2.15
2.2.14
2.2.13
2.2.12
2.2.11
2.2.10
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.9
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.8
2.0.7-patch2
2.0.7-patch
2.0.7
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.2.0
1.1.5
1.1.4
1.1.3-alpha
1.1.2-alpha
1.1.1-alpha
1.1.0-beta3
1.1.0-beta2
1.1.0-beta
1.0.9
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3