TokenMapper.java에 소스 추가
// 토큰 정보 가져가기
package com.example.mapper;
import org.apache.ibatis.annotations.Mapper;
import com.example.dto.TokenDTO;
@Mapper
public interface TokenMapper {
// 토큰 정보를 추가 또는 수정하기
public int upsertToken(TokenDTO obj);
// 토큰 정보 가져가기
public TokenDTO selectTokenOne(String userid);
public int deleteToken(String userid);
}
tokenMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.TokenMapper">
<select id="selectTokenOne" parameterType="String"
resultType="com.example.dto.TokenDTO">
SELECT T.* FROM TOKENTBL T WHERE USERID=#{userid}
</select>
<insert id="upsertToken" parameterType="com.example.dto.TokenDTO">
MERGE INTO
TOKENTBL <!-- 토큰테이블에 USERID가 일치 하는게 있다면 TOKEN, REGDATE를 업데이트 하고 -->
USING DUAL
ON USERID=#{userid}
WHEN MATCHED THEN
UPDATE SET TOKEN=#{token}, REGDATE=CURRENT_DATE
WHEN NOT MATCHED THEN <!-- 없다면 USERID,TOKEN,REGDATE를 추가한다 -->
INSERT (USERID, TOKEN, REGDATE ) VALUES(#{userid},#{token}, CURRENT_DATE)
</insert>
<!--회원 탈퇴에 쓸 것.-->
<delete id="deleteToken" parameterType="com.example.dto.TokenDTO">
DELETE FROM TOKENTBL WHERE USERID=#{userid}
</delete>
</mapper>
(회원 정보 수정) MemberRestController.java
@PutMapping(value = "/update.json")
public Map<String, Object> updatePUT(
@RequestBody MemberDTO member,
HttpServletRequest request,
HttpServletRequest httpServletRequest){
Map<String, Object> retMap = new HashMap<>();
try {
// 필터로 전송된 아이디 값 꺼내기
String userid = (String)request.getAttribute("userid");
member.setUserid(userid);
System.out.println(userid.toString());
int ret = mMapper.updateMember(member);
retMap.put("ret", ret);
retMap.put("status", 200);
} catch (Exception e) {
e.printStackTrace();
retMap.put("status", -1);
}
return retMap;
}
(회원 정보 수정) memberMapper.xml
<update id="updateMember" parameterType="com.example.dto.MemberDTO">
UPDATE MEMBERTBL SET
AGE=#{age}
<if test="phone != null"> <!-- 한개만 바꿔도 되고 두개만 바꿔도 되고 이럴땐 if 문으로-->
, PHONE=#{phone}
</if>
<if test="gender != null">
, GENDER=#{gender}
</if>
WHERE
USERID=#{userid}
</update>
(회원 정보 수정) MemberMapper.java
// 회원 정보 수정
public int updateMember(MemberDTO member);
(암호 변경) MemberDTO.java
package com.example.dto;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
// Map의 장점 변수 지정할 필요 없음
// 단점 : 형변환을 해야 함.
// mybatis에서는 그저 아무 의미없이 값을 보관하는 Map<String, Object> map
// DTO의 장점
// type이 정의 되어 있음
// 단점 : 필요시 변수를 만들어야 함.
@Getter // mybatis Mapper에서 사용함
@Setter // view에서 사용
@ToString() // 현재 객체의 내용 확인용
@NoArgsConstructor // 생성자
@AllArgsConstructor
public class MemberDTO {
String userid;
String userpw;
int age;
String phone;
String gender;
Date regdate;
String role;
int block = 1;
String userpw1; // 암호변경을 위해 추가
}
(암호 변경) MemberRestController.java
@PutMapping(value="/updatepw.json")
public Map<String, Object> updatepwPUT(
@RequestBody MemberDTO member,
@RequestHeader(name = "TOKEN") String token) {
Map<String, Object> retMap = new HashMap<>();
try {
String userid = jwtUtil.extractUsername(token);
member.setUserid(userid);
member.setUserpw1( bcpe.encode(member.getUserpw1()) );
System.out.println("=====================");
System.out.println(member.toString());
System.out.println("=====================");
System.out.println(token.toString());
System.out.println("=====================");
// 아이디를 이용해서 현재 암호를 가져옴.
MemberDTO member1 = mMapper.selectMemberOne(userid);
System.out.println(member1.toString());
System.out.println("=====================");
// 입력한 암호, hash된 DB암호 비교
System.out.println(member.getUserpw());
System.out.println("=====================");
System.out.println(member1.getUserpw());
if(bcpe.matches(member.getUserpw(), member1.getUserpw())){
int ret = mMapper.updateMemberPw(member);
retMap.put("result", ret);
}
retMap.put("status", 200);
}
catch(Exception e) {
e.printStackTrace();
retMap.put("status", -1);
}
return retMap;
}
(암호 변경) MemberMapper.java에 소스 추가
// 암호 변경
public int updateMemberPw(MemberDTO member);
(암호 변경) memberMapper.xml
<update id="updateMemberPw" parameterType="com.example.dto.MemberDTO">
UPDATE MEMBERTBL SET USERPW=#{userpw1} WHERE USERID=#{userid}
</update>
PutMapping과 Update query문으로 값을 null로 만들 예정
(회원 탈퇴) MemberRestController.java
@PutMapping(value="/delete.json")
public Map<String, Object> deletePUT(
@RequestBody MemberDTO member,
@RequestHeader(name = "TOKEN") String token) {
Map<String, Object> retMap = new HashMap<>();
try {
String userid = jwtUtil.extractUsername(token);
member.setUserid(userid);
System.out.println("=====================");
System.out.println(member.toString());
// 아이디를 이용해서 현재 암호를 가져옴.
MemberDTO member1 = mMapper.selectMemberOne(userid);
System.out.println("=========================");
System.out.println(member1.toString());
// 입력한 암호, hash된 DB암호 비교
if(bcpe.matches(member.getUserpw(), member1.getUserpw())){
// MEMBERTBL에서 사용자 정보를 변경
int ret = mMapper.deleteMember(member);
// UPDATE MEMBERTBL SET AGE=0, PHONE=null, GENDER =null WHERE USERID=#{userid}
// TOKENTBL에 userid에 해당하는 항목 삭제
int ret1 = tMapper.deleteToken(userid);
// DELETE FROM TOKENTBL WHERE USERID=#{userid}
retMap.put("result", ret);
retMap.put("token", ret1);
}
retMap.put("status", 200);
}
catch(Exception e) {
e.printStackTrace();
retMap.put("status", -1);
}
return retMap;
}
(회원 탈퇴) MemberMapper.java에 소스 추가
// 회원 삭제
public int deleteMember(MemberDTO member);
(회원 탈퇴) memberMapper.xml
<update id="deleteMember" parameterType="com.example.dto.MemberDTO">
UPDATE MEMBERTBL SET AGE=0, PHONE=null, GENDER=null WHERE USERID=#{userid}
</update>
(로그인) MemberRestController.java
@PostMapping(value = "/login.json")
public Map<String, Object> loginPOST(
@RequestBody MemberDTO member) {
System.out.println(member.toString());
Map<String, Object> retMap = new HashMap<>();
try {
// string 권한으로 collection으로 변경
String[] strRole = { member.getRole() };
Collection<GrantedAuthority> role = AuthorityUtils.createAuthorityList(strRole);
// CustomDetailsService와 같은 역할
UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(
member.getUserid(), member.getUserpw(), role);
authenticationManager.authenticate(upat);
String token = jwtUtil.generateToken(member.getUserid());
// TOKENTBL에 아이디가 존재 한다면 insert 없으면 update를 한 다음 토큰 전송(어떤 아이디가 어떤 토큰을 발행 했는지 토큰을 관리 해야 함.)
TokenDTO obj = new TokenDTO();
obj.setUserid(member.getUserid());
obj.setToken(token);
int ret = tMapper.upsertToken(obj);
retMap.put("status", 200);
if (ret == 1) {
retMap.put("token", token);
}
}
catch (Exception e) {
e.printStackTrace();
retMap.put("status", -1);
}
return retMap;
}
(로그인) MemberMapper.java
public MemberDTO selectMemberOne(String userid);
(로그인) memberMapper.xml
<select id="selectMemberOne" parameterType="String" resultType="com.example.dto.MemberDTO">
SELECT M.* FROM MEMBERTBL M WHERE M.USERID=#{userid}
</select>
(회원가입) MemberRestController.java
@PostMapping(value = "/join.json")
public Map<String, Object> joinPOST(
@RequestBody MemberDTO member) {
Map<String, Object> retMap = new HashMap<>();
System.out.println(member.toString());
try {
// 암호변경후 저장
member.setUserpw(bcpe.encode(member.getUserpw()));
int ret = mMapper.joinMember(member);
retMap.put("status", 200);
retMap.put("result", ret);
} catch (Exception e) {
e.printStackTrace();
retMap.put("status", -1);
}
return retMap;
}
}
(회원가입) MemberMapper.java
// 여기를 생략했기 때문에 XML 에서 찾아서 자동으로 수행됨.
public int joinMember ( MemberDTO member );
(회원가입) memberMapper.xml
<insert id="joinMember" parameterType="com.example.dto.MemberDTO">
INSERT INTO MEMBERTBL(USERID, USERPW, AGE, PHONE, GENDER, REGDATE, ROLE)
VALUES(#{userid}, #{userpw}, #{age}, #{phone}, #{gender}, CURRENT_DATE, #{role})
</insert>
JwtFilter.java
package com.example.jwt;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.filter.OncePerRequestFilter;
import com.example.dto.TokenDTO;
import com.example.mapper.TokenMapper;
//필터를 적용하는자 하는 url설정
@WebFilter(urlPatterns = {
"/api/member/update.json",
"/api/member/updatepw.json",
"/api/member/delete.json"
})
public class JwtFilter extends OncePerRequestFilter {
@Autowired TokenMapper tMapper;
@Autowired JwtUtil jwtUtil;
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
System.out.println("=======filter============");
System.out.println(request.getRequestURI());
System.out.println("=======filter============");
System.out.println("=======Token============");
String token = request.getHeader("TOKEN");
System.out.println("=======Token============");
if(token == null || token.length() == 0) {
throw new Exception(); // 강제로 오류발생 시킴
}
String userid = jwtUtil.extractUsername(token);
if(jwtUtil.validateToken(token, userid) == false){
throw new Exception();
}
// 세션에 저장된 토큰과 현재 토큰이 같은 것인지 확인
TokenDTO obj = tMapper.selectTokenOne(userid);
if(!obj.getToken().equals(token)){
throw new Exception();
}
// 필터 요소
// restcontoller에 전달하고 싶은 값들
request.setAttribute("userid", userid);
// restcontroller로 실행
filterChain.doFilter(request, response);
}
catch(Exception e) {
e.printStackTrace();
response.sendError(-1, "token error");
}
}
}
JwtUtil.java
package com.example.jwt;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@Component
public class JwtUtil {
// 토큰 생성용 보안키
private final String SECRETKEY = "fekjkfe43jfe";
// 정보 추출용 메소드
private <T> T extractClaim(String token, Function<Claims, T> claimsResolver){
final Claims claims = Jwts.parser().setSigningKey(SECRETKEY).parseClaimsJws(token).getBody();
return claimsResolver.apply(claims);
}
// 토큰 생성(아이디 정보를 이용한 토큰 생성)
public String generateToken(String username) {
System.out.println(username);
// ex) 30분 => 1000 * 60 * 30
long tokenValidTime = 1000 * 60 * 60 * 4; // 4시간
Map<String, Object> claims = new HashMap<>();
String token = Jwts.builder().setClaims(claims).setSubject(username)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + tokenValidTime))
.signWith(SignatureAlgorithm.HS256, SECRETKEY).compact();
return token;
}
// 토큰 검증
public Boolean validateToken(String token, String userid){
// 토큰에서 아이디 정보 추출
final String username = this.extractUsername(token);
if (username.equals(userid) && !isTokenExpired(token)) {
return true;
}
return false;
}
// 토큰에서 아이디 정보 추출하기
public String extractUsername(String token){
return extractClaim(token, Claims::getSubject);
}
// 토큰에서 만료 시간 추출하기
public Date extractExpiration(String token){
return extractClaim(token, Claims::getExpiration);
}
// 토큰의 만료시간이 유효한지 확인
public Boolean isTokenExpired(String token){
// 만료시간 가져와서 현재시간보다 이전인지 확인
return this.extractExpiration(token).before(new Date());
}
}