sharedPreferences 로 자동로그인 + 데이터 암호화 구현

 

[ 구현 목록]

 

1. check box를 통한 자동 로그인 ture/false 기능 구현

2. sharedPreferences로 로그인 데이터 저장

3. 로그인 데이터를 암호화해서 저장 

 

SharedPreferences의 저장방식

- 소규모의 데이터를 앱 안에 XML 파일로 저장하는 저장소

- 아래의 경로로 데이터 저장

/data/data/{package_name}/shared_prefs/{filename}.xml

- 안드로이드 스튜디오 디바이스 매니저에서 확인 가능 

 

Security Library 사용

https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences

 

EncryptedSharedPreferences  |  Android Developers

androidx.constraintlayout.core.motion.parse

developer.android.com

- 앱의 데이터를 쉽게 알아보게 저장하면 보안 문제 발생

- sharedPreferences를 암호화 할 수 있는 라이브러리 사용 

- Android keystore system을 사용해 키를 앱 내부가 아닌 시스템만이 접근 가능한 컨테이너에 저장

- 마스터키를 제작 후 EncryptedSharedPreferences로 암호화/복호화 가능

 

 

EncryptedSharedPreferences로 인스턴스 생성

private val sharedPreferences : SharedPreferences by lazy {
    val masterKeyAlias = MasterKey
        .Builder(applicationContext, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
        .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
        .build()
    EncryptedSharedPreferences.create(
        applicationContext,
        "encrypted_settings", //파일이름
        masterKeyAlias,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
    )
}

- MasterKey 클래스로 암호화에 사용할 마스터키 생성

- EncryptedSharedPreferences로 읽기쓰기에 사용할 sharedPreferences 인스턴스 생성

- 파일이름은 xml에 저장할 이름 

- 각각 key, value 암호화 진행 

- 사용 방법은 sharedPreferences와 같음 

 

Editor 생성

private val editor = sharedPreferences.edit()

이제 자동 로그인 기능을 구현한다.

 

❗️오류 발견  : applicationContext를 onCreate 내부에서 사용해야 한다.

 

lateinit var로 변수들을 선언한다.

 

private lateinit var sharedPreferences : SharedPreferences
private lateinit var editor : Editor

on Create 내부에서 masterKey와 sharedPreferences를 생성한다.

이 후에 editor를 생성해서 사용하면 된다.

// on create 내부에 선언
val masterKeyAlias = MasterKey
    .Builder(applicationContext, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build()

sharedPreferences  =
    EncryptedSharedPreferences.create(
        applicationContext,
        "encrypted_settings", //파일이름
        masterKeyAlias,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
    )

editor = sharedPreferences.edit()

 

우선 자동 로그인 체크 박스가 선택 되었는지 확인하는 함수를 제작한다. 

- 체크 되었다면 데이터를 추가

- 체크가 해제 되어있으면 데이터 삭제 

 

private fun checkSharedLogin(){
    //체크된 상태 확인
    if(binding.autoLoginCheckBox.isChecked){
        if (!checkAutoData()){
            setAutoData()
        }
    //해제
    }else{
        if (checkAutoData()) {
            removeAutoData()
        }
    }
}

 

자동 로그인 데이터가 존재하는지 확인하는 함수 : checkAutoData()

//아이디와 비밀번호가 존재하는지 확인
private fun checkAutoData() :Boolean{
    return sharedPreferences.getString("name","")!="" &&
            sharedPreferences.getString("pass","")!=""
}

 

자동 로그인이 체크 되어있고 자동 로그인 데이터가 존재하지 않으면 새로 데이터를 추가한다.

//자동 로그인 저장
private fun setAutoData(){
    editor.putString("name",getUserName())
    editor.putString("pass",getUserPass())
    editor.commit()
}

 

자동 로그인이 체크 되지않고 자동 로그인 데이터가 존재하면 해당 데이터를 삭제한다.

//자동 로그인 삭제
private fun removeAutoData(){
    editor.remove("name")
    editor.remove("pass")
    editor.clear()
    editor.commit()
}

 

login 버튼을 클릭했을 경우 login함수가 실행되는데, 이 때 checkSharedLogin() 함수를 실행한다.

 

[login() 함수]

    private fun login(){

        (application as MasterApplication).service.loginUser(
            getUserName(),getUserPass()
        ).enqueue(object :Callback<User>{
            override fun onResponse(call: Call<User>, response: Response<User>) {
                (application as MasterApplication).createRetrofit()
                if (response.isSuccessful && response.body()!=null){

                    //로그인 성공
                    val user = response.body()
                    //토큰 가져오기
                    val token = user!!.token
                    if (token!=null){
                        saveUserToken(token,this@LoginActivity)
                    }

                    //자동 로그인 확인
                    checkSharedLogin()

                    //다음 화면으로 넘어가기
                    val intent = Intent(applicationContext,MainActivity::class.java)
                    intent.putExtra("username",getUserName())
                    startActivity(intent)
                }else{
                    val err = errorConvert(response.errorBody())
                    Toast.makeText(this@LoginActivity,err,Toast.LENGTH_SHORT)
                        .show()
                }
            }

            override fun onFailure(call: Call<User>, t: Throwable) {
                Toast.makeText(this@LoginActivity,"Network error",Toast.LENGTH_SHORT)
                    .show()
            }

        })

    }

 

이제 자동 로그인이 체크 되었다면 데이터를 저장한 상태로 앱을 실행하고,

자동 로그인이 체크 되어있지 않다면 아무런 데이터를 저장하지 않은 상태가 된다.

 

새로 앱을 실행했거나 로그아웃 했을경우의 처리가 필요하다.

 

우선 새로 앱을 실행하면 로그인 데이터가 존재하는지 확인한다.

onCreate함수에서 checkAutoData() 함수를 실행한다.

새로 앱을 실행하면 체크박스가 체크되어 있지 않으므로 데이터를 저장하지 않고 검사만 진행한다.

 

//자동 로그인 체크 시 로그인
if (checkAutoData()){
    binding.autoLoginCheckBox.isChecked = true
    setUserName(sharedPreferences.getString("name",""))
    setUserPass(sharedPreferences.getString("pass",""))
    login()
}

 

만약 자동 로그인 데이터가 존재하면 해당 체크 박스를 true로 변경한다.

그 이후에 EditText의 값을 설정한다. 

private fun setUserName(name : String?){
    if(name!="") {
        binding.userNameInput.setText(name)
    }
}

private fun setUserPass(pass : String?){
    if(pass!="") {
        binding.userPasswordInput.setText(pass)
    }
}

이제 로그인 함수를 강제로 실행시켜서 자동 로그인을 진행한다.

 

마지막으로 로그아웃 시 동작을 해야한다.

로그아웃으로 LoginActivity로 돌아온다면 자동 로그인 코드로 인해서 다시 로그인이 될 것이다.

로그아웃 동작으로 액티비티가 실행 되었다면 기존의 자동 로그인 데이터를 삭제한다.

 

로그아웃을 진행하는 Fragment에서 액티비티 이동 시 logout data를 전달한다.

binding.logout.setOnClickListener {
    val intent = Intent(mainActivity, LoginActivity::class.java)
    intent.putExtra("logout",true)
    startActivity(intent)
}

 

로그인 액티비티에서 해당 데이터가 존재하면 자동 로그인 데이터를 체크하고 삭제한다.

val isLogout = intent.getBooleanExtra("logout",false)
//로그아웃으로 넘어왔다면 자동 로그인 삭제
if (isLogout){
    //데이터가 존재하면 삭제
    checkSharedLogin()
}

checkSharedLogin() 함수는 자동 로그인이 체크되지 않고 데이터가 존재하다면 삭제시킨다.

 

로그인 + 로그아웃 기능을 암호화해서 구현했다.