매번 Spring Security를 구현하기 귀찮은 사람들을 위한 JWT 토큰 기반 기본 세팅입니다.
크게 사용 가능한 기능은 아래와 같습니다.
- 회원가입
- 구글 소셜 로그인
- 로그인
- JWT 토큰 발행
- JWT 토큰 검증
- refresh-token 발행, 검증, 새로운 토큰 발행
- 유저 로그인 시 마지막 로그인 시간 기록
- CORS 설정
- 웹소켓 통신
먼저 main/resources/application.yml 파일을 적절히 수정해야합니다. 없다면 생성하세요.
application.yml 세팅 예시
file:
upload-dir: C:\uploads\profile-image # 파일이 업로드 되는 경로입니다.
download-url: http://localhost:8080/uploads/ # 파일을 찾으라고 요청할 URL입니다.
custom:
setting:
cors: http://localhost:5173 # CORS를 허가할 주소 입니다. 리액트는 :3000으로 바꾸세요.
server:
port: 8080 # server port
# db config
spring:
jwt:
secret: asdfie # 이 곳에 50자 이상 영문자를 무작위로 입력하세요. JWT 토큰 생성 비밀 키 입니다.
datasource:
driver-class-name: org.mariadb.jdbc.Driver # DB드라이버 입니다. MySQL 사용자는 적절히 수정하세요.
url: jdbc:mariadb://localhost:3306/security_test # DB URL입니다. security_test라는 데이터베이스가 존재해야합니다.
username: 데이터베이스 유저네임 입력
password: 데이터베이스 비밀번호 입력
devtools:
restart:
enabled: false
security:
oauth2:
client:
registration:
google:
client-id: 발급받은 client id
client-secret: 발급받은 클라이언트 보안 비밀번호
scope: openid,profile,email
# jpa config
jpa:
hibernate:
ddl-auto: create # 한 번 실행 후 update로 변경하는게 좋습니다.
properties:
hibernate:
show_sql: true # 서버가 SQL실행 시 콘솔에 출력 여부
format_sql: true # 출력할 SQL을 이쁘게 만들어주는지 여부
# Logging Level
logging:
level:
root: info # 기본 로그 설정
# org.springframework.security: TRACE # 주석을 풀어서 security 상세 로그 보기먼저 모든 Java script 예제 코드는 axios 라이브러리를 사용합니다. 없다면 설치하고 진행하세요.
resources/application.yml 세팅에 의하여 아래의 테이블을 자동으로 생성합니다.
테이블의 컬럼을 바꾸고 싶다면 com/website/entity/User.java에서 변경할 수 있습니다.
컬럼을 변경하려면 JWT 토큰 설정 등 여러 설정을 변경할 수 있어야합니다.
-
테이블 명: user
컬럼컬럼명 타입 설명 user_codeBIGINT (PK, AI) 유저 고유 키 user_idVARCHAR 로그인 ID (Unique) nicknameVARCHAR 닉네임 (Unique) passwordVARCHAR 암호화된 비밀번호 introduceVARCHAR 자기소개 roleVARCHAR 사용자 권한 profile_imgVARCHAR 프로필 이미지 경로 create_atDATETIME 가입일 last_login_timeDATETIME 마지막 로그인 시간 enableBOOLEAN 계정 활성 여부 oauth_providerVARCHAR 소셜 로그인 담당자 oauthIdVARCHAR 소셜 로그인 유일 값 emailVARCHAR 이메일 nameVARCHAR 이름(실명)
com/website/security/config/SecurityConfig.java 설정에서 변경할 수 있습니다.
자세한 사항은 주석을 참고하세요. 기본 설정은 아래와 같습니다.
-
모든 요청을 인가하는 url
/api로 시작하는 모든 요청 -
JWT 토큰 검증이 필요한 url
/auth로 시작하는 모든 요청 -
나머지 요청
모두Denied
- URL :
POST /api/user/signup - 설명 : 새로운 사용자를 등록합니다.
- Content-Type :
multipart/form-data
| 필드명 | 타입 | 필수 | 설명 |
|---|---|---|---|
userId |
string | ✔️ | 사용자 ID |
password |
string | ✔️ | 비밀번호 |
nickname |
string | ✔️ | 닉네임 |
회원가입은 com/website/user/UserController.java가 담당합니다.
userId, password, nickname을 받아 회원가입을 진행합니다.
각 필드가 비어있거나, userId 또는 nickname이 중복되었다면 BadRequest(400) 을 반환합니다.
회원가입 요청 테스트 JS는 아래와 같습니다.
const signup = () => {
const form = new FormData();
form.append('userId', '아이디값');
form.append('password', '비밀번호값');
form.append('nickname', '닉네임값');
axios.post('http://localhost:8080/api/user/signup', form)
}회원가입에 성공했다면 201 코드를 응답받습니다.
- URL :
POST /login - 설명 : JWT Access Token 및 Refresh Token 발급
- Content-Type :
multipart/form-data
| 필드명 | 타입 | 필수 | 설명 |
|---|---|---|---|
username |
string | ✔️ | 사용자 ID (userId) |
password |
string | ✔️ | 비밀번호 |
로그인은 com/website/security/jwt/LoginFilter.java가 담당합니다.
로그인 요청 테스트 JS는 아래와 같습니다.
const login = async () => {
const form = new FormData();
form.append('username', '아이디값');
form.append('password', '비밀번호값');
const res = await axios.post('http://localhost:8080/login', form);
console.log('JWT Token :',res.headers["authorization"]);
}로그인에 성공했다면 200 코드를 응답받습니다. 위 코드로 발급된 JWT 토큰을 확인할 수 있습니다.
여러 요청 테스트용 Controller를 제공합니다. 아래 코드를 사용하여 정상적인 요청이 가는지 확인할 수 있습니다.
const openUrlRequestTest = async () => { //모두가 접근 가능한 HTTP요청 테스트
const res = await axios.get('http://localhost:8080/api/test');
console.log(res);
}const authUrlRequestTest = async () => {
const res = await axios.get('http://localhost:8080/auth/test', {
headers:{
Authorization: '이 곳에 문자열로 /login 테스트를 진행하고 console에 출력된 JWT 토큰을 복사해서 넣으세요.'
}
});
console.log(res);
}테스트에 성공했다면 200코드를 응답받습니다.
- 서명 방식 :
HS256 (HMAC-SHA256) - Secret Key :
application.yml에 작성된 secret-key - 토큰이 갖고 있는 정보
userCode: 유저 기본 키userId: 유저 아이디role: 유저 권한
간단히 테스트 해볼 수 있는 React Component의 전체 코드는 아래와 같습니다.
직접 React코드를 수정해서 여러 요청을 해보세요.
React 테스트 코드
import axios from 'axios'
function App() {
const signupTest = () => {
const f = new FormData();
f.append('userId', 'test');
f.append('nickname', 'testNickname');
f.append('password', 'test')
const res = axios.post('http://localhost:8080/api/user/signup', f);
}
const loginTest = async () => {
const f = new FormData();
f.append('username', 'test');
f.append('password', 'test');
const res = await axios.post('http://localhost:8080/login', f);
console.log(res.headers["authorization"]);
}
const openUrlRequestTest = async () => {
const res = await axios.get('http://localhost:8080/api/test');
console.log(res);
}
const authUrlRequestTest = async () => {
const res = await axios.get('http://localhost:8080/auth/test', {
headers: {
Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhY2NvdW50Q29kZSI6MSwiYWNjb3VudElkIjoidGVzdCIsImFjY291bnRSb2xlIjoiUk9MRV9VU0VSIiwiaWF0IjoxNzUwMjg5OTQ0LCJleHAiOjE3NTAzMjU5NDR9.tEQwVIuT8jvKlf-Qgel-2v6tpHAdIBGXjNCt0qHaU08'
}
});
console.log(res);
}
return (
<div>
<button onClick={signupTest}>회원가입</button>
<button onClick={loginTest}>로그인</button>
<button onClick={openUrlRequestTest}>open api 경로</button>
<button onClick={authUrlRequestTest}>일반 경로</button>
<a href="http://localhost:8080/oauth2/authorization/google">
<button>Google로 로그인하기</button>
</a>
</div>
)
}
export default App- lombok
- mysqlDriver
- mariaDriver
- Spring data JPA
- Spring security
- Spring devtools
- WebSocket
- JWT 0.12.3
- oauth2-client
- Postman
- React