인증번호가 오고 자동으로 입력해주는 기능이 있는데 요즘 앱에서는 대부분 채용을 하는 것 같다.. ()
그래서 나도 해당 기능을 구현해볼려고 구글에서 검색을 해보고 직접 구현을 해 본 부분을 기록으로 남기고자 글을 적습니다.
SMS Retriever API를 사용한 자동 SMS 인증 | Credential Verification | Google Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Switch to English 의견 보내기 SMS Retriever API를 사용한 자동 SMS 인증 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분
developers.google.com
우선 dependencies에 추가를 해준다.
그리고 manifests에 등록할 BroadcastReceiver를 만들어 준다.
class MySmsReceiver : BroadcastReceiver() {
companion object {
var smsBroadcastReceiverListener: SmsBroadcastReceiverListener? = null
fun unBindListener() {
smsBroadcastReceiverListener = null
}
fun bindListener(smsBroadcastReceiverListener: SmsBroadcastReceiverListener) {
Companion.smsBroadcastReceiverListener = smsBroadcastReceiverListener
}
}
override fun onReceive(context: Context?, intent: Intent?) {
if (SmsRetriever.SMS_RETRIEVED_ACTION == intent!!.action) {
val extras = intent.extras
val status = extras?.get(SmsRetriever.EXTRA_STATUS) as Status
when (status.statusCode) {
CommonStatusCodes.SUCCESS -> {
var message = extras.getString(SmsRetriever.EXTRA_SMS_MESSAGE)
//todo 파싱을 해서 코드만 넘겨주도록 !
if (message != null) {
smsBroadcastReceiverListener?.onSuccess(message.trim { it <= ' ' })
}
}
CommonStatusCodes.TIMEOUT -> {
smsBroadcastReceiverListener?.onFailure()
}
}
}
}
}
그리고 manifests에서 application 태그 안에
<receiver
android:name=".broadcast.MySmsReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" />
</intent-filter>
</receiver>
해당 receiver를 등록해준다.
문자가 오면 이제 receiver가 해당 앱에서 캐치를 해야하는 문자인지 알 수 있게 하는 부분이 필요한데
이 때 11자리로 이루어진 hash를 쓰게 된다.
현재 근무하는 회사에서 오는 sms 양식은 이렇습니다.
[web발신]
인증번호 [ xxxxxx ] 을 입력해주세요.
hash11자리
대충 이런식으로 왔던 것 같다.
hash 11자리를 구하는 건 구글에서 검색하면 나옵니다. 그 중 많이 쓰는 코드를 올려놓겠습니다.
class AppSignatureHelper(context: Context) : ContextWrapper(context) {
val appSignatures: ArrayList<String>
@SuppressLint("PackageManagerGetSignatures") @RequiresApi(api = Build.VERSION_CODES.KITKAT) get() {
val appCodes = ArrayList<String>()
try {
val packageName = packageName
val packageManager = packageManager
val signatures = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures
signatures.mapNotNull { hash(packageName, it.toCharsString()) }.mapTo(appCodes) { String.format("%s", it) }
} catch (e: PackageManager.NameNotFoundException) {
Log.v(TAG, "Unable to find package to obtain hash.", e)
}
return appCodes
}
companion object {
val TAG = AppSignatureHelper::class.java.simpleName!!
private val HASH_TYPE = "SHA-256"
private val NUM_HASHED_BYTES = 9
private val NUM_BASE64_CHAR = 11
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private fun hash(packageName: String, signature: String): String? {
val appInfo = packageName + " " + signature
try {
val messageDigest = MessageDigest.getInstance(HASH_TYPE)
messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8))
var hashSignature = messageDigest.digest()
// truncated into NUM_HASHED_BYTES
hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES)
// encode into Base64
var base64Hash = Base64.getEncoder().encodeToString(hashSignature)
base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR)
Log.v(TAG + "sms_sample_test", String.format("pkg: %s -- hash: %s", packageName, base64Hash))
return base64Hash
} catch (e: NoSuchAlgorithmException) {
Log.v(TAG + "sms_sample_test", "hash:NoSuchAlgorithm", e)
}
return null
}
}
}
해당 코드는 쓰고 실제 product라면 지우시는 걸 추천합니다.
그리고 playstore에 올리신다면 playstore 용 hash를 따로 구하셔야합니다.
해당 블로그에 잘 설명되어 있습니다 !
https://jinchoi.oopy.io/android/etc/2
SMS Retriever API로 인증번호 자동 입력하기
유저에게 인증번호 자동 입력 기능을 제공하려면 구글의 SMS Retriever API를 활용해야 한다. 본 포스팅에서는 SMS Retriever API 연동 과정, 이를 위해 필요한 요구사항들, sms hash 계산 방법 및 유의사항
jinchoi.oopy.io
window에서 구할려고 하면 뭔가 복잡한거 같아서 microdoft store에서 터미널 받으시고 우분투를 받으신다음 우분투 터미널에서 구하시는게 훨씬 쉬웠습니다.
class MainActivity : AppCompatActivity() {
private var client: SmsRetrieverClient? = null
private var task: Task<Void>? = null
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
initClient()
task!!.addOnSuccessListener { startSMSListener() }
task!!.addOnFailureListener { e -> // Failed to start retriever, inspect Exception for more details
}
}
private fun initClient() {
client = SmsRetriever.getClient(this)
task = client!!.startSmsRetriever()
}
private fun setAuthCode(authCode : String){
binding.tvMsg.text = authCode
}
private fun startSMSListener() {
MySmsReceiver.bindListener(object : SmsBroadcastReceiverListener {
override fun onSuccess(authCode: String) {
setAuthCode(authCode)
initClient()
}
override fun onFailure() {
initClient()
}
})
}
override fun onDestroy() {
super.onDestroy()
MySmsReceiver.unBindListener()
}
}
이게 저만 그런건지는 모르겠는데 정상적으로 실행이 되면 receiver에서 onSuccess로 code가 옵니다.
그리고 나서 receiver를 다시 등록을 해주지 않으면 다음 문자부터는 또 읽어오지를 않습니다.
그래서 다시 initClient로 receiver를 붙여줬습니다.
그리고 destroy 될 때 receiver를 해제해주셔야 합니다.
틀린 부분이 있을 수도 있습니다 ! 참고해주세요 !
틀린 부분이 있으면 알려주시면 감사하겠습니다.
'android' 카테고리의 다른 글
Android Compose Navigation 기초 2 - Argument 보내기 (1) | 2023.11.06 |
---|---|
Android Compose Navigation 기초 (0) | 2023.11.06 |