Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 9th_Android
Submodule 9th_Android updated from f0ed26 to a3cac9
9 changes: 9 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import org.gradle.kotlin.dsl.annotationProcessor

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
Expand Down Expand Up @@ -69,4 +71,11 @@ dependencies {
implementation("androidx.room:room-ktx:$roomVersion")
kapt("androidx.room:room-compiler:$roomVersion")

implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.retrofit2:adapter-rxjava2:2.9.0")

implementation("com.github.bumptech.glide:glide:4.11.0")
kapt("com.github.bumptech.glide:compiler:4.11.0")

}
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/java/com/example/umc/AuthResponse.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example.umc

import com.google.gson.annotations.SerializedName


data class AuthResponse(
@SerializedName("isSuccess") val isSuccess: Boolean,
@SerializedName("code") val code: Int,
@SerializedName("message") val message: String,
@SerializedName("result") val result: Result?

)

data class Result(
@SerializedName("userIdx") val userIdx: Int,
@SerializedName("jwt") val jwt: String
)
14 changes: 14 additions & 0 deletions app/src/main/java/com/example/umc/AuthRetrofitInterface.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.umc

import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST

interface AuthRetrofitInterface {
@POST("/users")
fun signUp(@Body user: User): Call<AuthResponse>

@POST("/users/login")
fun login(@Body user: User): Call<AuthResponse>

}
36 changes: 36 additions & 0 deletions app/src/main/java/com/example/umc/AuthService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.example.umc

import android.util.Log
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class AuthService {
private lateinit var signUpView: SignUpView

fun setSignUpView(signUpView: SignUpView) {
this.signUpView = signUpView
}
fun signUp(user: User){
val authService = getRetrofit().create(AuthRetrofitInterface::class.java)

authService.signUp(user).enqueue(object : Callback<AuthResponse> {
override fun onResponse(call: Call<AuthResponse>, response: Response<AuthResponse>) {
if (response.isSuccessful && response.code() == 200) {
val resp: AuthResponse = response.body()!!

Log.d("SIGNUP/SUCCESS", response.toString())
when (resp.code) {
1000 -> signUpView.onSignUpSuccess()
else -> signUpView.onSignUpFailure()
}
}
}

override fun onFailure(call: Call<AuthResponse>, t: Throwable) {
}
})
Log.d("SIGNUP", "Hello")
}

}
18 changes: 18 additions & 0 deletions app/src/main/java/com/example/umc/NetworkModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.example.umc

import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

const val BASE_URL = "https://edu-api-test.softsquared.com"

fun getRetrofit(): Retrofit {

val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()

return retrofit
}
6 changes: 6 additions & 0 deletions app/src/main/java/com/example/umc/SignUpView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.umc

interface SignUpView {
fun onSignUpSuccess()
fun onSignUpFailure()
}
66 changes: 60 additions & 6 deletions app/src/main/java/com/example/umc/SignupActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package com.example.umc

import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.umc.databinding.ActivitySignupBinding
import retrofit2.Call
import retrofit2.Response

class SignupActivity : AppCompatActivity() {
class SignupActivity : AppCompatActivity(), SignUpView {

lateinit var binding: ActivitySignupBinding

Expand All @@ -29,7 +32,23 @@ class SignupActivity : AppCompatActivity() {

}

private fun signUp() {
// private fun signUp() {
// if (binding.signUpIdEt.text.toString().isEmpty() || binding.signUpDirectInputEt.text.toString().isEmpty()) {
// Toast.makeText(this, "이메일 형식이 잘못되었습니다.", Toast.LENGTH_SHORT).show()
// return
// }
// if (binding.signUpPasswordEt.text.toString().isEmpty() || binding.signUpPasswordCheckEt.text.toString().isEmpty()) {
// Toast.makeText(this, "비밀번호가 일치하지 않습니다.", Toast.LENGTH_SHORT).show()
// return
// }
// val userDB = SongDatabase.getInstance(this)!!
// userDB.userDao().insert(getUser())
//
// val users = userDB.userDao().getUsers()
// Log.d("SIGNUPACT", users.toString())
// }

private fun signUp(){
if (binding.signUpIdEt.text.toString().isEmpty() || binding.signUpDirectInputEt.text.toString().isEmpty()) {
Toast.makeText(this, "이메일 형식이 잘못되었습니다.", Toast.LENGTH_SHORT).show()
return
Expand All @@ -38,13 +57,48 @@ class SignupActivity : AppCompatActivity() {
Toast.makeText(this, "비밀번호가 일치하지 않습니다.", Toast.LENGTH_SHORT).show()
return
}
val userDB = SongDatabase.getInstance(this)!!
userDB.userDao().insert(getUser())
// val authService = getRetrofit().create(AuthRetrofitInterface::class.java)
// authService.signUp(getUser()).enqueue(object : retrofit2.Callback<AuthResponse> {
// override fun onResponse(
// call: Call<AuthResponse?>,
// response: Response<AuthResponse?>
// ) {
// Log.d("SIGNUP/SUCCESS", response.toString())
// val resp: AuthResponse = response.body()!!
//
// when(resp.code){
// 1000 -> finish()
// 2016,2018 ->{
// binding.signUpEmailErrorTv.visibility = View.VISIBLE
// binding.signUpEmailErrorTv.text = resp.message
// }
// }
// }
//
// override fun onFailure(
// call: Call<AuthResponse?>,
// t: Throwable
// ) {
// Log.d("SIGNUP/FAILURE", t.message.toString())
// }
//
//
// })
// Log.d("SIGNUP/SUCCESS", "Hello")

val authService = AuthService()
authService.setSignUpView(this)

authService.signUp(getUser())
}

val users = userDB.userDao().getUsers()
Log.d("SIGNUPACT", users.toString())
override fun onSignUpSuccess() {
finish()
}

override fun onSignUpFailure() {

}
}


76 changes: 76 additions & 0 deletions keyword/chapter08/keyword
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
- OAuth2 방식
- OAuth2란 무엇일까요?

사용자가 자신의 계정정보를 직접 제공하지 않고도, 제3자 애플리케이션(앱)이 특정서비스에 대한 접근 권한을 위임받을 수 있도록 하는 인증 및 권한 부여를 위한 개방형 표준 프로토콜

동작원리

1. 사용자가 클라이언트 앱에서 서비스 C의 계정으로 로그인하려고 합니다.
2. 클라이언트 앱은 인증 서버에 접근 권한을 요청합니다. (클라이언트 ID 및 접근 권한 범위를 전달)
3. 인증 서버는 사용자에게 로그인을 요청하고, 클라이언트 앱이 요청한 접근 권한에 대한 동의를 받습니다.
4. 사용자가 동의하면, 인증 서버는 클라이언트 앱에 **접근 토큰**을 발급합니다.
5. 클라이언트 앱은 이 접근 토큰을 이용해 리소스 서버에 접근하여 데이터를 요청하고 사용합니다. 이 과정에서 사용자 계정의 비밀번호는 노출되지 않습니다.
- Token이란 무엇일까요?

서버가 각각의 클라이언트를 누군지 정확히 구별할 수 있도록 유니크한 정보를 담은 암호화 데이터 (유저 구별이 가능해야, 유저의 권한에 맞는 정확한 기능을 응답할 수 있기 때문 = 사용자 구분 및 정보유출 방지)

서버가 클라이언트 인증을 확인하는 방식은 대표적으로 쿠키,세션,토큰 3가지 방식이 있다.

https://im-gu-ma.tistory.com/2

- OAuth2에서 사용되는 토큰은 어떤 것이 있고, 각각 어떤 용도일까요?
1. Access Token
- 사용자의 권한을 증명하는 데 사용
- 클라이언트가 인증 서버로부터 발급받아, 사용자 정보를 포한한 보호된 리소스에 접근할 때 사용
2. Refresh Token
- Access Token의 만료 후 새로운 Access Token을 발급받기 위해 사용
- Access Token보다 더 긴 만료 시간을 가지며, 클라이언트가 안전한 환경에서 Access Token을 갱신할 수 있도록 합니다.
- OAuth2 방식의 장점과 단점은 무엇일까요?

**장점**

- **개인정보 보호**: 사용자가 직접 서비스의 로그인 정보(아이디, 비밀번호)를 제3자 애플리케이션에 제공하지 않으므로, 개인정보 유출 위험을 줄일 수 있습니다.
- **편의성**: '카카오 로그인', '구글로 로그인하기'처럼 여러 서비스를 한 번의 인증으로 편리하게 연동하고 사용할 수 있습니다.
- **확장성**: 마이크로서비스 환경 등에서 다양한 애플리케이션을 유연하게 연동하고 확장하기 용이합니다.
- **보안 강화**: 별도의 세션 관리가 필요 없으며, 토큰 기반으로 사용자를 인증하므로 보안이 강화될 수 있습니다.

**단점**

- **구현의 복잡성**: OAuth 2.0을 제대로 구현하고 설정하는 것이 복잡할 수 있으며, 특히 보안 관련 구성에 주의해야 합니다.
- **토큰 유출 시 보안 위험**: 액세스 토큰이 유출될 경우 심각한 보안 문제가 발생할 수 있으므로, HTTPS 사용 등 안전한 토큰 관리 전략이 필수적입니다.
- Cookie와 Session
- Cookie란 무엇일까요?

http의 일종으로 사용자가 어떤 웹사이트를 방문할 경우, 해당 사이트가 사용하고 있는 서버에서 사용자의 컴퓨터에 저장하는 작은 기록 정보 파일

- **`Key-Value`쌍으로 구성**되어 있는 데이터 파일이다.
- **쿠키이름, 쿠키값, 만료시간, 전송할 도메인명, 전송할 경로, 보안연결여부, HttpOnly여부로 구성**되어 있다.
- **도메인 당 20개의 쿠키**를 가질 수 있다.
- **하나의 쿠키는 4KB(= 4096 byte)까지 저장이 가능**하다.

![image.png](attachment:6964b49e-2687-4646-9402-1144a4376e0a:image.png)

- Session이란 무엇일까요?

브라우저가 종료되기 전까지 클라이언트의 요청을 유지하게 해주는 기술

- 웹 서버에 **웹 컨테이너의 상태를 유지하기 위한 정보를 저장**한다.
- 웹 서버에 저장되는 쿠키(세션 쿠키 / session cookie)이다.
- 브라우저를 닫거나, 서버에서 세션을 삭제했을 때만 삭제가 되기 때문에 **쿠키보다 비교적 보안적으로 우수하다.**
- **저장 데이터에 제한이 없다.**(서버 용량 허용 범위 내에서)
- 각 클라이언트에 **고유 세션 ID(Session ID)를 부여**한다. 세션 ID를 통해 클라이언트를 구분하여 각 요구에 맞는 서비스를 제공한다.
- https://velog.io/@octo__/%EC%BF%A0%ED%82%A4Cookie-%EC%84%B8%EC%85%98Session

![image.png](attachment:4d4f894f-313a-4dc6-99ed-d9fcc6970b31:image.png)

- JWT
- JWT (JSON Web Token)은 무엇일까요?

사용자 인증 및 정보 공유를 위해 사용되는 json형식의 토큰

이 토큰은 사용자의 정보(클레임)를 자체적으로 포함하고 있으며, 서명으로 암호화되어 있어 위변조를 방지합니다. JWT는 주로 RESTful과 같은 무상태(Stateless) 웹 환경에서 사용자 데이터를 안전하게 주고받기 위해 사용됩니다.

- JWT 방식의 토큰은 어떤 구조로 되어있고, 각각 어떤 용도로 사용되나요?
1. **Header**: 토큰의 타입(JWT)과 서명에 사용된 알고리즘(HS256, RS256 등)이 담겨 있습니다.
2. **Payload**: 토큰에 담을 사용자 정보(클레임)가 담겨 있습니다. 여기에는 사용자의 아이디, 권한 등이 포함될 수 있습니다.
3. **Signature**: 헤더와 페이로드를 합쳐 비밀 키를 통해 서명한 결과입니다. 이 서명을 통해 토큰의 무결성을 검증합니다