시도 | 성공 | |
회원가입 | - email 및 패스워드 양식 확인 | - salt 부여 및 DB salt 테이블에 저장 - salt + 패스워드 해싱해서 DB member 테이블에 저장 |
로그인 | - 입력 받은 email로 salt 조회 - 입력 받은 패스워드 + 조회된 salt로 해싱 - DB에 저장된 패스워드 해시값과 비교 |
- salt + email 해싱해서 토큰 발급 - 토큰을 login 테이블에 저장 (email, token, 생성시간) - 모든 요청에서 토큰으로 사용자 인증 |
회원가입
Member 테이블 생성
create table member(
email varchar(50) primary key,
pwd varchar(256) not null,
nickname varchar(20) not null unique,
registDate timestamp default current_timestamp
);
MemberDto
package com.community.used.dto;
import java.util.Date;
public class Member {
private String email, pwd, nickname;
private Date registDate;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Date getRegistDate() {
return registDate;
}
public void setRegistDate(Date registDate) {
this.registDate = registDate;
}
public Member() {
super();
// TODO Auto-generated constructor stub
}
public Member(String email, String pwd, String nickname, Date registDate) {
super();
this.email = email;
this.pwd = pwd;
this.nickname = nickname;
this.registDate = registDate;
}
@Override
public String toString() {
return "Member [email=" + email + ", pwd=" + pwd + ", nickname=" + nickname + ", registDate=" + registDate
+ "]";
}
}
MemberDao
package com.community.used.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import com.community.used.dto.Member;
@Mapper
public interface MemberDao {
public Member login(Member m) throws Exception;
public void insertMember(Member m) throws Exception;
public void updateMember(Member m) throws Exception;
public void deleteMember(String email) throws Exception;
}
MemberService
package com.community.used.service;
import java.util.Date;
import java.util.UUID;
import java.util.regex.Pattern;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.community.used.dao.LoginDao;
import com.community.used.dao.MemberDao;
import com.community.used.dao.SaltDao;
import com.community.used.dto.Login;
import com.community.used.dto.Member;
import com.community.used.dto.SaltInfo;
import com.community.used.util.OpenCrypt;
@Service
public class MemberService {
@Autowired
MemberDao memberDao;
@Autowired
LoginDao loginDao;
@Autowired
SaltDao saltDao;
public Login checkToken(String authorization) throws Exception {
// TODO Auto-generated method stub
return loginDao.checkToken(authorization);
}
public Login tokenLogin(Member m) throws Exception {
String pwd = m.getPwd();
String salt = saltDao.checkSalt(m.getEmail());
byte[] pwdOriginalHash=OpenCrypt.getSHA256(pwd, salt);
String pwdHash=OpenCrypt.byteArrayToHex(pwdOriginalHash);
m.setPwd(pwdHash);
m=memberDao.login(m);
if(m!=null) {
String nickname=m.getNickname();
if(nickname!=null && !nickname.trim().equals("")) {
//member table에서 email과 pwd가 확인된 상황 즉 login ok
String email=m.getEmail();
//2. email을 hashing 한다
byte[] EmailOriginalHash=OpenCrypt.getSHA256(email, salt);
//3. db에 저장하기 좋은 포맷으로 인코딩한다
String myToken=OpenCrypt.byteArrayToHex(EmailOriginalHash);
//4. login table에 token 저장
Login loginInfo=new Login(email, myToken, nickname, new Date());
loginDao.insertToken(loginInfo);
return loginInfo;
}
}
return null;
}
public Member login(Member m) throws Exception {
return memberDao.login(m);
}
public void insertMember(Member m) throws Exception{
// 이메일 유효성 검사
String email=m.getEmail();
if (!isValidEmail(email)) {
throw new Exception("유효하지 않은 이메일 형식입니다.");
}
// 패스워드 유효성 검사
String pwd=m.getPwd();
if (!isValidPassword(pwd)) {
throw new Exception("패스워드는 8자리 이상이어야 하며, 특수문자와 숫자를 포함해야 합니다.");
}
// 패스워드 암호화
//1. salt를 생성한다
String salt=UUID.randomUUID().toString();
//2. pwd를 hashing 한다
byte[] originalHash=OpenCrypt.getSHA256(pwd, salt);
//3. db에 저장하기 좋은 포맷으로 인코딩한다
String pwdHash=OpenCrypt.byteArrayToHex(originalHash);
m.setPwd(pwdHash);
saltDao.insertSalt(new SaltInfo(email, salt));
memberDao.insertMember(m);
}
public void updateMember(Member m) throws Exception{
memberDao.updateMember(m);
}
public void deleteMember(String email) throws Exception{
memberDao.deleteMember(email);
}
public void logout(String authorization) throws Exception {
loginDao.deleteToken(authorization);
}
// 이메일 유효성 검사 메서드
private boolean isValidEmail(String email) {
// 이메일 패턴
String emailPattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
return Pattern.matches(emailPattern, email);
}
// 패스워드 유효성 검사 메서드
private boolean isValidPassword(String password) {
// 패스워드 패턴: 8자리 이상, 숫자 포함, 특수문자 포함
String passwordPattern = "^(?=.*[0-9])(?=.*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?]).{8,}$";
return Pattern.matches(passwordPattern, password);
}
}
MemberController
package com.community.used.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import com.community.used.dto.Login;
import com.community.used.dto.Member;
import com.community.used.service.MemberService;
@RestController
@CrossOrigin("http://127.0.0.1:5500/")
public class MemberController {
@Autowired
MemberService memberService;
@PostMapping("logout")
public void logout(@RequestHeader String authorization) {
try {
memberService.logout(authorization);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@PostMapping("login")
public Map<String,String> tokenLogin(@RequestBody Member m) {
Map<String,String> responseMap=new HashMap<>();
try {
Login loginInfo=memberService.tokenLogin(m);
if(loginInfo!=null && loginInfo.getNickname()!=null && loginInfo.getToken()!=null) {
responseMap.put("nickname", loginInfo.getNickname());
responseMap.put("Authorization", loginInfo.getToken());
}else {
responseMap.put("msg", "다시 로그인 해주세요1");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
responseMap.put("msg", "다시 로그인 해주세요2");
}
return responseMap;
}
@PostMapping("insertMember")
public String insertMember(@RequestBody Member m) {
try {
memberService.insertMember(m);
return m.getNickname()+"님 가입을 환영합니다";
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "회원 가입 실패";
}
}
@PostMapping("updateMember")
public String updateMember(@RequestBody Member m) {
try {
memberService.updateMember(m);
return "ok";
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "email과 pwd 확인해 주세요";
}
}
@PostMapping("deleteMember")
public String deleteMember(@RequestBody String email) {
try {
memberService.deleteMember(email);
return "ok";
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "email과 pwd 확인해 주세요";
}
}
}
member.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.community.used.dao.MemberDao">
<select id="login" resultType="Member" parameterType="Member">
select * from member where email=#{email} and pwd=#{pwd}
</select>
<insert id="insertMember" parameterType="Member">
insert into member(email, pwd, nickname) values(#{email}, #{pwd}, #{nickname})
</insert>
<update id="updateMember" parameterType="Member">
update member set nickname=#{nickname} where email=#{email} and pwd=#{pwd}
</update>
<delete id="deleteMember" parameterType="String">
delete from member where email=#{email}
</delete>
</mapper>
패스워드 암호화
saltInfo 테이블 생성
create table saltInfo(
email varchar(50) primary key,
salt varchar(256) );
slafInfoDto
package com.community.used.dto;
public class SaltInfo {
private String email, salt;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public SaltInfo(String email, String salt) {
super();
this.email = email;
this.salt = salt;
}
public SaltInfo() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "SaltInfo [email=" + email + ", salt=" + salt + "]";
}
}
slatDao
package com.community.used.dao;
import org.apache.ibatis.annotations.Mapper;
import com.community.used.dto.SaltInfo;
@Mapper
public interface SaltDao {
public void insertSalt(SaltInfo saltInfo) throws Exception;
public String checkSalt(String email) throws Exception;
}
salt.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.community.used.dao.SaltDao">
<insert id="insertSalt" parameterType="com.community.used.dto.SaltInfo">
INSERT INTO saltInfo (email, salt)
VALUES (#{email}, #{salt})
</insert>
<select id="checkSalt" parameterType="String" resultType="String">
SELECT salt FROM saltInfo WHERE email = #{email}
</select>
</mapper>
로그인
login 테이블 생성
CREATE TABLE `ureca`.`login` (
`email` VARCHAR(50) NOT NULL primary key,
`token` VARCHAR(256) NOT NULL unique,
`logintime` TIMESTAMP NOT NULL DEFAULT current_timestamp);
LoginDto
package com.community.used.dto;
import java.util.Date;
public class Login {
private String email, token, nickname;
private Date loginTime;
public Login(String email, String token, String nickname, Date loginTime) {
super();
this.email = email;
this.token = token;
this.nickname = nickname;
this.loginTime = loginTime;
}
public Login() {
super();
// TODO Auto-generated constructor stub
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Date getLoginTime() {
return loginTime;
}
public void setLoginTime(Date loginTime) {
this.loginTime = loginTime;
}
@Override
public String toString() {
return "Login [email=" + email + ", token=" + token + ", nickname=" + nickname + ", loginTime=" + loginTime
+ "]";
}
}
LoginDao
package com.community.used.dao;
import org.apache.ibatis.annotations.Mapper;
import com.community.used.dto.Login;
@Mapper
public interface LoginDao {
public void insertToken(Login login) throws Exception;
public void deleteToken(String token) throws Exception;
public Login checkToken(String authorization) throws Exception;
}
login.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.community.used.dao.LoginDao">
<insert id="insertToken" parameterType="Login">
insert into login(email, token) values(#{email},#{token})
</insert>
<delete id="deleteToken" parameterType="String">
delete from login where token=#{token}
</delete>
</mapper>
Github
GitHub - leeemingyu/used-community-back
Contribute to leeemingyu/used-community-back development by creating an account on GitHub.
github.com
'[LG 유플러스] 유레카 > 프로젝트' 카테고리의 다른 글
[번개장터 클론코딩][Spring Boot] 이미지 저장 방식, 사용자 인증, 로그인 유효시간 및 토큰 관리 (0) | 2025.03.19 |
---|---|
[번개장터 클론코딩][Spring Boot] 회원가입 시 이메일 및 닉네임 중복 검사 구현 (0) | 2025.03.18 |
[번개장터 클론코딩][JS] 기본 구조, 로그인, 회원가입 구현, SPA (0) | 2025.03.15 |