<span th:text="${#temporals.format(goodsReview.getCreatedAt(), 'yyyy-MM-dd HH:mm')}"></span>

 

특정 날짜를 불러올 때, 원하는 날짜 형식으로 표현하고 싶을 때, 타임리프로 다음과 같이 설정한다.

 

y는 년도, M은 월, d는 일, H는 시간, m은 분, s는 초

 

2024-12-06 01:05:22  <ㅡ 이와 같은 형식으로 날짜를 나타낸다.

 

CREATE TABLE `worldcup`.`tournaments`
(
    `index`                   INT UNSIGNED   NOT NULL AUTO_INCREMENT,         #인덱스(PK)
    `user_email`               VARCHAR(50)    NOT NULL,        #해당 토너먼트 만든 유저 이메일(FK)
    `thumbnail`                LONGBLOB   NOT NULL,        #해당 대회의 썸네일
    `thumbnail_file_name`       VARCHAR(256)   NOT NULL,        #썸네일 파일 이름
    `thumbnail_content_type`    VARCHAR(50)    NOT NULL,        #썸네일 타입
    `title`                   VARCHAR(50)    NOT NULL,        #대회 이름
    `content`                 VARCHAR(1000)  NOT NULL,        #대회 설명
    `play_count`               INT UNSIGNED   NOT NULL DEFAULT 0,       #플레이 횟수
    `created_at`               DATETIME   NOT NULL DEFAULT NOW(),       #만든날짜
    `modified_at`              DATETIME   NULL DEFAULT NULL,    #수정날짜
    `is_recognize`             BOOLEAN    NOT NULL DEFAULT FALSE,       #승인여부
    CONSTRAINT PRIMARY KEY (`index`),
    CONSTRAINT FOREIGN KEY (`user_email`) REFERENCES `worldcup`.`users` (`email`)   #FK설정 (이메일 삭제되어도 토너먼트는 삭제안됨)
);

 

일단 DB 테이블을 다음과 같이 작성한다. 썸네일 부분을 보면 썸네일 데이터를 저장하는 `thumbnail`, 썸네일 파일 이름을 저장하는 `thumbnail_file_name`, 썸네일의 타입(jpg 같은것)을 저장하는 `thumbnail_content_type`이 있다.

 

 

 

tournament.setThumbnail(thumbnail.getBytes());
tournament.setThumbnailFileName(thumbnail.getOriginalFilename());
tournament.setThumbnailContentType(thumbnail.getContentType());

멀티 파트 파일 형식으로 해당 이미지 파일을 불러와서 엔티티에 다음과 같이 정의하고 db에 저장한다.

 

 

 

그럼 db에 다음과 같이 이미지 데이터가 저장되었다.

 

 

 

// 토너먼트 선택
public TournamentEntity get(int index) {
    return this.tournamentMapper.selectTournamentByIndex(index);
}

먼저 해당 데이터를 불러오는 Mapper를 만든 후, 서비스에서 다음과 같이 작성하고

 

 

@RequestMapping(value = "/thumbnail", method = RequestMethod.GET)
public ResponseEntity<byte[]> getTournamentThumbnail(@RequestParam("index") int index) {
    TournamentEntity tournament = this.tournamentService.get(index);
    if (tournament == null) {
        return ResponseEntity.notFound().build();   // Not Found (404)
    }
    return ResponseEntity.ok()  // OK (200)
            .contentType(MediaType.parseMediaType(tournament.getThumbnailContentType()))
            .contentLength(tournament.getThumbnail().length)
            .body(tournament.getThumbnail());
}

컨트롤러 부분에 가서 다음과 같이 작성한다. /thumbnail?index=1 주소로 가면 1번 인덱스의 데이터의 썸네일이 불려오는 것을 확인할 수 있다.

비밀번호 암호화

 

 

암호화를 하는데 사용하는 기능은 SHA-512, BCrypt 가 있는데, SHA-512 기능은 성능이 너무 좋아 권장하지 않는다. GPU의 연산속도가 빠르기 때문에 공격자의 하드웨어를 통한 오프라인 brute force에 더 취약하다. 빠른 해시를 사용하여 암호화를 진행시 공격자는 오프라인 공격으로 초당 수십억개의 해시를 계산할 수 있다.

 

그래서 요즘은 BCrypt를 이용하여 비밀번호 암호화를 하는 편이다.

 

 

 

 

암호화 된 비밀번호를 비교하는 방법, BCrypt.checkpw(비밀번호, 비밀번호) 를 사용한다.

 

 

@Transactional

이 어노테이션은 서비스 함수를 실행할 때 어느 한 곳에 문제가 발생하였을 경우 그 전에 진행되었던 모든 작업을 원래대로 리셋한다. 돈을 송금하는 ATM기를 예를 들어 설명하면, 일단 돈을 보냈으면 본인의 통장에 돈이 빠져나갔을 것이다. 하지만 전송하는데 실패를 했을 경우 받을 사람의 통장에 돈이 들어가지 않았을 것이다. 그럼 본인의 통장에도 돈이 빠져나갔고, 받을 사람의 통장에도 돈이 들어오지 않았으니 문제가 된다. 이를 방지하기 위해 돈 송금 실패시, 모든 작업을 리셋하여 보낸 사람의 통장에 보냈던 돈을 다시 소유하게끔 하는 작업..

 

 

스프링 시큐리티 사용 시, 애플리케이션 부분에 해당 코드를 작성하여, 자동으로 인증되도록 한다.

 

 

 

 

 

MVN 사이트에 가서 Spring Boot Starter Mail을 pom.xml에 추가 (버전은 지운다)

 

 

 

 

 

 

이메일에 보내질 인증번호 html 페이지를 다음과 같이 작성한다.

 

 

 

 

 

 

서비스로 가서 이메일을 보내는 로직을 다음과 같이 작성한다. 

 

 

 

 

 

구글 계정관리에 들어가서 앱 비밀번호를 등록 후 비밀번호를 복사해 놓는다.

 

 

 

 

 

어플리케이션 프로퍼티에 다음과 같이 작성한다. 이때 유저 이메일은 위에서 로직에 작성한 이메일과 동일하게 하고, 패스워드는 복사해놓은 비밀번호를 붙여넣기 한다.

 

 

 

 

 

 

이메일을 보낼 수 있는 계정에 인증번호를 보내고 확인해보면 다음과 같이 메일이 온 것을 확인할 수 있다.

 

 

 

 

 

정규식을 불러오는 클래스 Regex

 

 

 

 

 

 

원하는 정규식을 다음과 같이 작성한다. @UtilityClass라는 롬복 어노테이션은 해당 클래스를 다른 클래스에서 객체화 불가능하게 한다.

 

 

 

 

 

 

 

HTML에서 다음과 같이 작성한다. th:with 로 원하는 정규식이 있는 경로를 불러오고 th:attrappend로 원하는 input 태그에 원하는 정규식을 집어넣는다.

 

 

 

 

 

 

개발자도구에 들어가서 HTML을 확인해본 결과 알맞게 불러왔다.

 

 

 

일단 로그인을 만들었다. 로그인창에서 db에 있는 데이터값을 넣고 로그인을 누르면 해당 홈페이지로 이동하게끔 하도록 했다. 하지만 그건 로그인 된 것이 아니다. 다시 로그인 창으로 이동해서 다른 데이터 값을 넣어도 해당 홈페이지로 이동한다. 그렇다. 세션을 통해 로그인 정보를 기억하게끔 해야 로그인 상태를 유지할 수 있다.

 

 

 

먼저 로그인 서비스로 가서 메서드에 파라미터 값으로 불러온 로그인 유저(내가 보낸 정보)에 setter로 db에서 불러온 해당 유저의 정보들을 전부 넣고, 컨트롤러에 전달한다.

 

 

 

 

먼저 컨트롤러에 가서 로그인 서버에 파라미터로 HttpSession 을 추가하고, 만약 로그인이 성공적으로 끝났다면 setAttribute로 해당 로그인 유저의 정보들을 넣고, "user"라는 이름으로 명명한다. 그리고 홈페이지로 리다이렉트 하도록 하였다.

 

 

 

 

html로 가서 만약 'user'라는 이름의 해당 세션이 null 값이 아닐 경우(로그인 되어있는 경우) 이미 로그인 되어있다는 메시지창과 함께, 홈페이지로 넘어가게끔 하였다.

 

 

 

 

홈 페이지로 가면 해당 세션의 유저 객체가 null이 아닐경우 해당 유저의 닉네임을 화면에 보여지도록 하였다.

 

 

 

 

그 결과 비로그인 유저라면 홈페이지를 접속했을 시, 위 화면이 뜨고 /user/login에 접속해서 로그인 하고 홈페이지에 접속했을 시 화면에 로그인 유저의 닉네임이 뜨도록 되어 있다.

 

 

 

 

만약 로그인 되어 있는데도 /user/login 에 접속했다면 위 메세지가 뜨면서 자동으로 홈페이지로 가게 되어있다.

 

 

 

 

 

 

로그아웃을 구현하는건 간단하다. HttpSession을 불러와서 setAttribute로 이미 만들어진 "user"라는 이름의 세션에 null 값을 넣어주면 로그인이 풀리게 된다. 그리고 /user/login 주소로 리다이렉트 하게끔 하였다.

 

 

 

 

간단 설명

평문 -> SHA-512 -> a~f 0~9랜덤으로 128자

SHA-512는 필연적으로 평문에 훼손이 간다. 즉 128자로 전환은 되는데 전환된 128자가 원래 평문으로 돌아갈 순 없다. 즉 복호화 못하게끔 조치를 취한 것이다. 이를 일방향 암호화라고 한다.

 

 

 

예시

 

자바에서 SHA-512를 구현하는 방법이다. utils 디렉터리 안에 CryptoUtil 클래스를 다음과 같이 만들어, 뭔가를 복호화 시키고 싶은 경우 해당 클래스의 hashSha512 메서드를 호출하면 된다.

 

 

 

 

 

솔트값을 복호화 시키고 싶다. 만약 해당 솔트값을 이메일 + 코드 + 랜덤소수 + 랜덤소수로 이뤘으면 한다, 이를 모두 복호화 하고 싶으면 이런식으로 복호화를 진행하면 된다.

 

JDBC 방식을 사용하기로 하였다. 먼저 Util에서 자바와 데이터베이스간의 연동을 시킨다.

 

 

 

 

DAO는 DB에 데이터를 저장하는 작업을 수행하는 객체다. insert 메서드에 Memo 엔티티를 파라미터로 넣어 해당 쿼리문안에 엔티티로 불러온 값들을 대조하여 쿼리를 실행시킨다.

 

 

 

 

 

서비스를 통해 Dao 로 쿼리문을 실행할 메서드를 만든다.

 

 

 

 

 

 

그리고 컨트롤러에 가서 서비스 객체를 불러온 후, 서비스 객체 안의 add() 메서드를 실행시킨다. 안에 메모 엔티티를 파라미터로 넣는 것 역시 필수

 

 

 

 

 

작성하면 DB에 추가되는 것을 볼 수 있다.

 

자바에서 JSONObject를 사용하기 위해 MVN에서 JSON in Java를 찾은 후 최신버전을 pom.xml의 dependences에 추가한다.

 

 

 

 

 

JSONOBJECT에 result 값을 추가한 후 POSTMAN으로 파라미터를 넣어 확인 결과, 다음과 같이 오브젝트 값을 받았다.

 

 

 

 

 

미디어타입을 이용한다면 해당 값이 오브젝트 값인지 확인시켜준다.

 

 

 

 

 

formData를 이용하여 xhr 요청으로 formData로 값을 넣고 전송하면 해당 주소에 파라미터로 전달된다.(반드시 name이랑 같은 이름으로 전송한다.) 그리고 응답한 결과는 역시 오브젝트 형식으로 { 'result' : 값 }.. 이를 바탕으로 값 결과를 나타낼 수 있다.

 

 

 

1. 자동로그인

유저 테이블을 만들고...

 

 

 

자동 로그인을 하기 위해서는 위의 persistent_login테이블을 완전히 저것과 똑같이 만들어야 한다.

 

 

 

자동 로그인은 rememberMe()를 사용한다. 토큰을 발급하고, 토큰의 유효기간 역시 설정할 수 있다.

 

 

자동로그인 체크박스를 만들고 name 요소에 remember-me 라고 설정.. 반드시 이 이름으로 설정해야 한다.

 

 

 

자동 로그인을 하면 테이블에 자동 로그인된 아이디를 발견할 수 있다. 만약 로그아웃을 할 시, 테이블에 데이터가 사라진다.

 

 

 

2. 암호화 해시 함수 

패스워드 엔코더 구현체인 NoOpPasswordEncoder는 암호를 인코딩하지 않고 일반 텍스트로 유지한다. 암호를 해시하지 않기 때문에 보안관련해서 취약하므로 실무에서는 절대 사용해선 안된다. 그래서 BCryptPasswordEncoder를 사용하기로 했다. 이것은 블로피시 암호에 기반을 둔 암호화 해시 함수이고, 패스워드 해싱 시 내부적으로 랜덤한 salt값을 생성하여 매번 다른 해싱 결과를 출력한다.

tom으로 회원가입을 했을 시, 패스워드가 암호화 된것 확인할 수 있다. 만약 또 다른 계정으로 회원가입을 하고 패스워드를 똑같이 123으로 설정해 놓으면, tom의 password와 다른 값으로 암호화 된걸 확인할 수 있다.

 

 

 

3. 기타

인증 된 경우에만 태그를 볼 수 있게 하는 방법이 있다. 주로 sec:authorize="isAuthenticated()" 구문을 사용한다. if문을 이용하여 특정 유저만 태그를 볼 수 있게끔 하는 방법 역시 존재한다. 게시물1에서 계정명이 tom일 경우 삭제버튼을 볼 수 있게 해놓았고, 현재 james 계정으로 로그인을 한 상태이다. tom 계정이 아니기 때문에 이 설정을 한 삭제 버튼을 볼 수 없는 것이다.

 

 

 

로그아웃을 하고 본 경우, 인증을 거쳐야만 볼 수 있는 태그들을 볼 수 없다.

+ Recent posts