Kaynağa Gözat

登录添加图片验证码,并且密码加密字符串

fancy 5 yıl önce
ebeveyn
işleme
6b1093da7a
16 değiştirilmiş dosya ile 460 ekleme ve 20 silme
  1. 122 15
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/login/LoginActivity.kt
  2. 26 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/login/LoginContract.kt
  3. 85 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/login/LoginPresenter.kt
  4. 1 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityActivity.kt
  5. 5 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityContract.kt
  6. 42 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityPresenter.kt
  7. 41 0
      o2android/app/src/main/res/layout/activity_login.xml
  8. 1 0
      o2android/app/src/main/res/values/strings.xml
  9. 30 3
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/api/service/OrgAssembleAuthenticationService.kt
  10. 1 1
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/api/service/OrgAssemblePersonalService.kt
  11. 12 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/CaptchaImgData.kt
  12. 14 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/LoginModeData.kt
  13. 14 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/LoginWithCaptchaForm.kt
  14. 11 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/RSAPublicKeyData.kt
  15. 2 1
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/main/person/PersonPwdForm.kt
  16. 53 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/CryptRSA.java

+ 122 - 15
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/login/LoginActivity.kt

@@ -1,6 +1,7 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.login
 
 import android.content.Context
+import android.graphics.BitmapFactory
 import android.media.AudioManager
 import android.media.MediaPlayer
 import android.os.Bundle
@@ -17,11 +18,11 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.bind.BindPhoneActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.main.MainActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.CaptchaImgData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.LoginModeData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.LoginWithCaptchaForm
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.AuthenticationInfoJson
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.BitmapUtil
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.DateHelper
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric.BioConstants
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric.BiometryManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric.OnBiometryAuthCallback
@@ -85,11 +86,21 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
     private var loginType = 0 // 0默认的用户名验证码登录 1用户名密码登录
     private var canBioAuth = false //是否有指纹认证
 
+    //验证码
+    private var useCaptcha = true
+    private var captcha : CaptchaImgData? = null
+
 
-    override fun afterSetContentView(savedInstanceState: Bundle?) {
-        receivePhone = intent.extras?.getString(REQUEST_PHONE_KEY) ?: ""
 
+    override fun afterSetContentView(savedInstanceState: Bundle?) {
+        //获取加密key
+        mPresenter.getRSAPublicKey()
+        //获取图片验证码
+        mPresenter.getCaptcha()
+        //
+        mPresenter.getLoginMode()
 
+        receivePhone = intent.extras?.getString(REQUEST_PHONE_KEY) ?: ""
         setDefaultLogo()
         login_edit_username_id.setText(receivePhone)
         tv_login_copyright.text = getString(R.string.copy_right).plus(" ")
@@ -114,23 +125,29 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
                 view_login_password_bottom.setBackgroundColor(FancySkinManager.instance().getColor(this, R.color.z_color_input_line_blur))
             }
         }
-
-
+        edit_login_captcha_input.setOnFocusChangeListener { _, hasFocus ->
+            if (hasFocus) {
+                view_login_captcha_input_bottom.setBackgroundColor(FancySkinManager.instance().getColor(this, R.color.z_color_input_line_focus))
+            }else {
+                view_login_captcha_input_bottom.setBackgroundColor(FancySkinManager.instance().getColor(this, R.color.z_color_input_line_blur))
+            }
+        }
 
         btn_login_submit.setOnClickListener(this)
         btn_bio_auth_login.setOnClickListener(this)
         tv_user_fallback_btn.setOnClickListener(this)
         tv_bioauth_btn.setOnClickListener(this)
+        image_login_captcha.setOnClickListener(this)
 
         //是否开启了指纹识别登录
         checkBioAuthLogin()
         if (BuildConfig.InnerServer) {
             login_edit_password_id.setHint(R.string.activity_login_password)
             login_edit_password_id.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
-//            InputType.TYPE_TEXT_VARIATION_PASSWORD
             button_login_phone_code.gone()
             tv_rebind_btn.gone()
             tv_bioauth_btn.gone()
+            ll_login_captcha.visible()
         }else {
             tv_bioauth_btn.visible()
             login_edit_password_id.setHint(R.string.login_code)
@@ -139,6 +156,8 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
             button_login_phone_code.setOnClickListener(this)
             tv_rebind_btn.visible()
             tv_rebind_btn.setOnClickListener(this)
+            //不需要图片验证码
+            ll_login_captcha.gone()
         }
 
     }
@@ -202,6 +221,10 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
                     reBindService()
                 })
             }
+            R.id.image_login_captcha -> {
+                showLoadingDialog()
+                mPresenter.getCaptcha()
+            }
         }
     }
 
@@ -228,11 +251,13 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
     }
     private  fun changeLoginType() {
         if (loginType == 0) {
+            ll_login_captcha.visible()
             login_edit_password_id.setHint(R.string.activity_login_password)
             login_edit_password_id.inputType = InputType.TYPE_TEXT_VARIATION_PASSWORD
             button_login_phone_code.gone()
             loginType = 1
         }else {
+            ll_login_captcha.gone()
             login_edit_password_id.setHint(R.string.login_code)
             login_edit_password_id.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_NORMAL
             button_login_phone_code.visible()
@@ -242,6 +267,32 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
     }
 
 
+    override fun loginMode(mode: LoginModeData?) {
+        if (mode != null) {
+            useCaptcha = mode.captchaLogin
+            if (useCaptcha) {
+                ll_login_captcha.visible()
+            }else {
+                ll_login_captcha.gone()
+            }
+        }
+    }
+
+    override fun showCaptcha(data: CaptchaImgData) {
+        hideLoadingDialog()
+        captcha = data
+         val stream = Base64ImageUtil.generateBase642Inputstream(data.image)
+        if (stream != null) {
+            image_login_captcha.setImageBitmap(BitmapFactory.decodeStream(stream))
+        }
+    }
+
+    override fun getCaptchaError(err: String) {
+        hideLoadingDialog()
+        //todo 图片验证码没有
+        XLog.error(err)
+    }
+
     override fun loginSuccess(data: AuthenticationInfoJson) {
         if (login_main_biometry.visibility == View.VISIBLE) {
             playBeepSound()
@@ -280,16 +331,72 @@ class LoginActivity: BaseMVPActivity<LoginContract.View, LoginContract.Presenter
             XToast.toastShort(this, "$label 不能为空!")
             return
         }
-        showLoadingDialog()
-        if (BuildConfig.InnerServer) {
-            mPresenter.loginByPassword(credential, code)
+        if (useCaptcha) {
+            if (BuildConfig.InnerServer) {
+//                mPresenter.loginByPassword(credential, code)
+                val captchaCode = edit_login_captcha_input.text.toString()
+                if (TextUtils.isEmpty(captchaCode)) {
+                    XToast.toastShort(this, "图片验证码 不能为空!")
+                    return
+                }
+                if (captcha == null) {
+                    XToast.toastShort(this, "图片验证码 不能为空!")
+                    return
+                }
+                val form = LoginWithCaptchaForm()
+                form.credential = credential
+                form.password = code
+                form.captcha = captcha!!.id
+                form.captchaAnswer = captchaCode
+                showLoadingDialog()
+                mPresenter.loginWithCaptcha(form)
+            }else {
+                if (loginType == 0) {
+                    showLoadingDialog()
+                    mPresenter.login(credential, code)
+                }else {
+//                    mPresenter.loginByPassword(credential, code)
+                    val captchaCode = edit_login_captcha_input.text.toString()
+                    if (TextUtils.isEmpty(captchaCode)) {
+                        XToast.toastShort(this, "图片验证码 不能为空!")
+                        return
+                    }
+                    if (captcha == null) {
+                        XToast.toastShort(this, "图片验证码 不能为空!")
+                        return
+                    }
+                    val form = LoginWithCaptchaForm()
+                    form.credential = credential
+                    form.password = code
+                    form.captcha = captcha!!.id
+                    form.captchaAnswer = captchaCode
+                    showLoadingDialog()
+                    mPresenter.loginWithCaptcha(form)
+                }
+            }
         }else {
-            if (loginType == 0) {
-                mPresenter.login(credential, code)
+            if (BuildConfig.InnerServer) {
+//                mPresenter.loginByPassword(credential, code)
+                val form = LoginWithCaptchaForm()
+                form.credential = credential
+                form.password = code
+                showLoadingDialog()
+                mPresenter.loginWithCaptcha(form)
             }else {
-                mPresenter.loginByPassword(credential, code)
+                if (loginType == 0) {
+                    showLoadingDialog()
+                    mPresenter.login(credential, code)
+                }else {
+//                    mPresenter.loginByPassword(credential, code)
+                    val form = LoginWithCaptchaForm()
+                    form.credential = credential
+                    form.password = code
+                    showLoadingDialog()
+                    mPresenter.loginWithCaptcha(form)
+                }
             }
         }
+
     }
 
 

+ 26 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/login/LoginContract.kt

@@ -2,6 +2,9 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.login
 
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseView
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.CaptchaImgData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.LoginModeData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.LoginWithCaptchaForm
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.AuthenticationInfoJson
 
 /**
@@ -13,11 +16,19 @@ object LoginContract {
         fun getCodeError()
         fun loginSuccess(data: AuthenticationInfoJson)
         fun loginFail()
+        fun showCaptcha(data: CaptchaImgData)
+        fun getCaptchaError(err: String)
+        fun loginMode(mode: LoginModeData?)
     }
 
     interface Presenter: BasePresenter<View> {
 
 
+        /**
+         * 登录方式
+         */
+        fun getLoginMode()
+
         /**
          * 获取验证码
          * @param value 用户名或者手机号码
@@ -37,6 +48,21 @@ object LoginContract {
 
         fun ssoLogin(userId: String)
 
+        /**
+         * 获取图片验证码
+         */
+        fun getCaptcha()
+
+
+        /**
+         * 图片验证码登录
+         */
+        fun loginWithCaptcha(form: LoginWithCaptchaForm)
+
+        /**
+         *
+         */
+        fun getRSAPublicKey()
 
     }
 }

+ 85 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/login/LoginPresenter.kt

@@ -1,11 +1,14 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.login
 
 
+import android.text.TextUtils
 import net.muliba.accounting.app.ExceptionHandler
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.ResponseHandler
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.LoginWithCaptchaForm
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.Base64ImageUtil
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.CryptRSA
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
 import rx.android.schedulers.AndroidSchedulers
@@ -17,8 +20,90 @@ import rx.schedulers.Schedulers
 
 class LoginPresenter : BasePresenterImpl<LoginContract.View>(), LoginContract.Presenter {
 
+    //rsa加密公钥
+    private var publicKey: String = ""
 
 
+    override fun getLoginMode() {
+        getAssembleAuthenticationService(mView?.getContext())?.let { service ->
+            service.loginMode()
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            mView?.loginMode(it.data)
+                        }
+                        onError { e, _ ->
+                            XLog.error("", e)
+                            mView?.loginMode(null)
+                        }
+                    }
+        }
+    }
+
+    override fun getCaptcha() {
+        getAssembleAuthenticationService(mView?.getContext())?.let { service ->
+            service.getCaptchaCodeImg(120, 50)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            if (it.data != null) {
+                                mView?.showCaptcha(it.data)
+                            }else {
+                                mView?.getCaptchaError("没有获取到图片验证码")
+                            }
+                        }
+                        onError { e, _ ->
+                            XLog.error("获取图片验证码错误,", e)
+                            mView?.getCaptchaError("没有获取到图片验证码")
+                        }
+                    }
+        }
+    }
+
+    override fun loginWithCaptcha(form: LoginWithCaptchaForm) {
+        getAssembleAuthenticationService(mView?.getContext())?.let { service ->
+            //加密
+            if (!TextUtils.isEmpty(publicKey)) {
+                XLog.debug("key:$publicKey")
+                val newPwd = CryptRSA.rsaEncryptByPublicKey(form.password, publicKey)
+                if (!TextUtils.isEmpty(newPwd)) {
+                    form.password = newPwd
+                    form.isEncrypted = "y"
+                    XLog.debug("加密成功。。。。。$newPwd")
+                }
+            }
+            service.loginWithCaptchaCode(form)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe(ResponseHandler { data -> mView?.loginSuccess(data) },
+                            ExceptionHandler(mView?.getContext()) { e ->
+                                XLog.error("", e)
+                                mView?.loginFail()
+                            })
+        }
+    }
+
+    override fun getRSAPublicKey() {
+        getAssembleAuthenticationService(mView?.getContext())?.let { service ->
+            service.getRSAPublicKey()
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            if (it.data!=null) {
+                                publicKey = it.data.publicKey
+                                XLog.debug("public key is ok.lllll ")
+                            }
+                        }
+                        onError { e, _ ->
+                            XLog.error("public key is error ", e)
+                        }
+                    }
+        }
+    }
+
     override fun getVerificationCode(value: String) {
         getAssembleAuthenticationService(mView?.getContext())?.let { service ->
             service.getPhoneCode(value)

+ 1 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityActivity.kt

@@ -23,6 +23,7 @@ class AccountSecurityActivity : BaseMVPActivity<AccountSecurityContract.View, Ac
     override var mPresenter: AccountSecurityContract.Presenter = AccountSecurityPresenter()
     override fun layoutResId(): Int  = R.layout.activity_account_security
     override fun afterSetContentView(savedInstanceState: Bundle?) {
+        mPresenter.getRSAPublicKey()
         setupToolBar(getString(R.string.title_activity_account_security), true)
 
         account_name_id.text = O2SDKManager.instance().cName

+ 5 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityContract.kt

@@ -15,5 +15,10 @@ object AccountSecurityContract {
     interface Presenter : BasePresenter<View> {
         fun logout(deviceId:String)
         fun updateMyPassword(old: String, newPwd: String, newPwdConfirm: String)
+        /**
+         *
+         */
+        fun getRSAPublicKey()
+
     }
 }

+ 42 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityPresenter.kt

@@ -1,11 +1,13 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.security
 
+import android.text.TextUtils
 import net.muliba.accounting.app.ExceptionHandler
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.ResponseHandler
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.exception.O2ResponseException
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.IdData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.person.PersonPwdForm
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.CryptRSA
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
 import rx.android.schedulers.AndroidSchedulers
@@ -13,6 +15,28 @@ import rx.schedulers.Schedulers
 
 class AccountSecurityPresenter : BasePresenterImpl<AccountSecurityContract.View>(), AccountSecurityContract.Presenter {
 
+    //rsa加密公钥
+    private var publicKey: String = ""
+
+
+    override fun getRSAPublicKey() {
+        getAssembleAuthenticationService(mView?.getContext())?.let { service ->
+            service.getRSAPublicKey()
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            if (it.data!=null) {
+                                publicKey = it.data.publicKey
+                                XLog.debug("public key is ok.lllll ")
+                            }
+                        }
+                        onError { e, _ ->
+                            XLog.error("public key is error ", e)
+                        }
+                    }
+        }
+    }
 
     override fun logout(deviceId: String) {
         getAssembleAuthenticationService(mView?.getContext())?.let { service->
@@ -29,6 +53,24 @@ class AccountSecurityPresenter : BasePresenterImpl<AccountSecurityContract.View>
         val service = getAssemblePersonalApi(mView?.getContext())
         if (service != null) {
             val form = PersonPwdForm(old, newPwd, newPwdConfirm)
+
+            if (!TextUtils.isEmpty(publicKey)) {
+                XLog.debug("key:$publicKey")
+                val newOld = CryptRSA.rsaEncryptByPublicKey(old, publicKey)
+                if (!TextUtils.isEmpty(newOld)) {
+                    form.oldPassword = newOld
+                }
+                val newNewPwd = CryptRSA.rsaEncryptByPublicKey(newPwd, publicKey)
+                if (!TextUtils.isEmpty(newNewPwd)) {
+                    form.newPassword = newNewPwd
+                }
+                val newNewPwdConfirm = CryptRSA.rsaEncryptByPublicKey(newPwdConfirm, publicKey)
+                if (!TextUtils.isEmpty(newNewPwdConfirm)) {
+                    form.confirmPassword = newNewPwdConfirm
+                }
+                form.isEncrypted = "y"
+            }
+
             service.modifyCurrentPersonPassword(form)
                     .subscribeOn(Schedulers.io())
                     .observeOn(AndroidSchedulers.mainThread())

+ 41 - 0
o2android/app/src/main/res/layout/activity_login.xml

@@ -153,6 +153,47 @@
                         android:background="@color/z_color_input_line_blur"
                         />
 
+                    <LinearLayout
+                        android:id="@+id/ll_login_captcha"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="10dp"
+                        android:orientation="horizontal">
+                        <LinearLayout
+                            android:layout_width="0dp"
+                            android:layout_weight="1"
+                            android:layout_height="match_parent"
+                            android:orientation="vertical">
+                            <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.ClearTextEdit
+                                android:id="@+id/edit_login_captcha_input"
+                                android:layout_width="match_parent"
+                                android:layout_height="49dp"
+                                android:layout_gravity="center_vertical"
+                                android:background="@null"
+                                android:hint="@string/login_captcha"
+                                android:maxLines="1"
+                                android:inputType="number"
+                                android:textColor="@color/z_color_text_primary"
+                                android:textColorHint="@color/z_color_text_hint"
+                                android:textSize="13sp" />
+                            <View
+                                android:id="@+id/view_login_captcha_input_bottom"
+                                android:layout_width="match_parent"
+                                android:layout_height="1dp"
+                                android:background="@color/z_color_input_line_blur"
+                                />
+                        </LinearLayout>
+                        <ImageView
+                            android:id="@+id/image_login_captcha"
+                            android:layout_width="120dp"
+                            android:layout_height="50dp"
+                            android:scaleType="fitCenter"
+                            />
+
+                    </LinearLayout>
+
+
+
                     <Button
                         android:id="@+id/btn_login_submit"
                         android:layout_width="match_parent"

+ 1 - 0
o2android/app/src/main/res/values/strings.xml

@@ -112,6 +112,7 @@
     <string name="activity_login_username">用户名或手机号码</string>
     <string name="login_phone">输入手机号码</string>
     <string name="login_code">验证码</string>
+    <string name="login_captcha">图片验证码</string>
     <string name="activity_login_password">密码</string>
     <string name="activity_login_submit">登    录</string>
     <string name="activity_login_other">其他登录方式</string>

+ 30 - 3
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/api/service/OrgAssembleAuthenticationService.kt

@@ -1,8 +1,6 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.service
 
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.ApiResponse
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.IdData
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.ValueData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.AuthenticationInfoJson
 import retrofit2.http.*
 import rx.Observable
@@ -15,6 +13,13 @@ import rx.Observable
 
 interface OrgAssembleAuthenticationService{
 
+
+    /**
+     * 登录状态
+     */
+    @GET("jaxrs/authentication/mode")
+    fun loginMode(): Observable<ApiResponse<LoginModeData>>
+
     /**
      * 登陆
      * @param json
@@ -62,6 +67,21 @@ interface OrgAssembleAuthenticationService{
     fun getPhoneCode(@Path("credential") credential: String): Observable<ApiResponse<ValueData>>
 
 
+    /**
+     * 获取图片验证码
+     */
+    @GET("jaxrs/authentication/captcha/width/{width}/height/{height}")
+    fun getCaptchaCodeImg(@Path("width") width: Int, @Path("height") height: Int): Observable<ApiResponse<CaptchaImgData>>
+
+
+    /**
+     * 用图片验证码登录
+     */
+    @Headers("Content-Type:application/json;charset=UTF-8")
+    @POST("jaxrs/authentication/captcha")
+    fun loginWithCaptchaCode(@Body form:LoginWithCaptchaForm): Observable<ApiResponse<AuthenticationInfoJson>>
+
+
     /**
      * 扫一扫 确认登录
      * @param meta
@@ -72,6 +92,13 @@ interface OrgAssembleAuthenticationService{
     @POST("jaxrs/authentication/bind/meta/{meta}")
     fun scanConfirmWebLogin(@Path("meta") meta: String): Observable<ApiResponse<AuthenticationInfoJson>>
 
+    /**
+     * 获取RSA 加密公钥
+     */
+    @GET("jaxrs/authentication/captchaRSAPublicKey")
+    fun getRSAPublicKey(): Observable<ApiResponse<RSAPublicKeyData>>
+
+
     /**
      * 登出
      * @return

+ 1 - 1
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/api/service/OrgAssemblePersonalService.kt

@@ -51,6 +51,6 @@ interface OrgAssemblePersonalService {
     /**
      * 更新当前用户密码
      */
-    @PUT("jaxrs/password")
+    @PUT("jaxrs/person/password")
     fun modifyCurrentPersonPassword(@Body body: PersonPwdForm): Observable<ApiResponse<ValueData>>
 }

+ 12 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/CaptchaImgData.kt

@@ -0,0 +1,12 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api
+
+/**
+ * Created by fancyLou on 2020-09-27.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+
+
+data class CaptchaImgData (
+        var id: String = "",
+        var image: String = "" //验证码图片的base64
+)

+ 14 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/LoginModeData.kt

@@ -0,0 +1,14 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api
+
+/**
+ * Created by fancyLou on 2020-09-27.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+
+
+data class LoginModeData (
+        var captchaLogin: Boolean = false, //验证码登录
+        var codeLogin: Boolean = false, //短信验证码登录
+        var bindLogin: Boolean = false,
+        var faceLogin: Boolean = false
+)

+ 14 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/LoginWithCaptchaForm.kt

@@ -0,0 +1,14 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api
+
+/**
+ * Created by fancyLou on 2020-09-27.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+
+data class LoginWithCaptchaForm (
+        var credential: String = "",
+        var password: String = "",
+        var captcha: String ="", //图片认证编号id
+        var captchaAnswer: String =  "",//图片认证码
+        var isEncrypted: String = "" //是否启用加密
+)

+ 11 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/RSAPublicKeyData.kt

@@ -0,0 +1,11 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api
+
+/**
+ * Created by fancyLou on 2020-09-27.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+
+
+data class RSAPublicKeyData (
+        var publicKey: String = "" //公钥
+)

+ 2 - 1
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/main/person/PersonPwdForm.kt

@@ -9,5 +9,6 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.person
 class PersonPwdForm(
         var oldPassword: String = "",
         var newPassword: String = "",
-        var confirmPassword: String = ""
+        var confirmPassword: String = "",
+        var isEncrypted: String = ""
 )

+ 53 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/CryptRSA.java

@@ -0,0 +1,53 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils;
+
+
+import android.util.Base64;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+
+import javax.crypto.Cipher;
+
+/**
+ * Created by fancyLou on 2020-09-27.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class CryptRSA {
+    /**RSA算法*/
+    private static final String RSA = "RSA";
+
+    private static KeyFactory keyFactory;
+    static {
+        //实例化密钥工厂
+        try {
+            keyFactory = KeyFactory.getInstance(RSA);
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 公钥加密
+     *
+     * @param content   待加密数据
+     * @param publicKey 密钥
+     * @return string 加密后数据
+     */
+    public static String rsaEncryptByPublicKey(String content, String publicKey) throws Exception {
+        byte[] keyBytes = Base64.decode(publicKey, Base64.NO_WRAP);
+        //初始化公钥
+        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
+        //产生公钥
+        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
+        //数据加密
+        //PKCS1Padding
+        Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding");
+        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
+        return new String(Base64.encode(cipher.doFinal(content.trim().getBytes("utf-8")), Base64.NO_WRAP));
+
+    }
+
+
+}