구글 계정 관리 → 보안 → 보안 수준이 낮은 앱의 엑세스 허용
table 만들어서 cmd창에 복사&붙여넣기
설정 잡기
pom.xml
mapper.xml 수정
application.properties
파일 추가
log4jdbc.log4j2.properties
logback-spring.xml
생성자 주입(@RequiredArgsConstructor), 새터 주입, 필드 주입(@Autowired)
어떤 DI가 바람직할까?
롬복을 이용해 생성자 주입을 하자
SecurityConfig.java
package com.example.demo;
import org.springframework.beans.factory.annotation.*;
import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.method.configuration.*;
import org.springframework.security.config.annotation.web.builders.*;
import org.springframework.security.config.annotation.web.configuration.*;
import org.springframework.security.crypto.password.*;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().antMatchers("/**").permitAll();
}
}
Zboard6Appication.java
package com.example.demo;
import org.mybatis.spring.annotation.*;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@MapperScan("com.example.demo.dao")
@SpringBootApplication
public class Zboard6Application {
public static void main(String[] args) {
SpringApplication.run(Zboard6Application.class, args);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
MemberController.java
package com.example.demo.controller;
import java.lang.reflect.Member;
import java.time.LocalDate;
import org.springframework.http.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import com.example.demo.controller.editor.DatePropertyEditor;
import com.example.demo.service.MemberService;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Controller
public class MemberController {
private final MemberService service;
// 사용자 입력을 가지고 command 객체를 생성하는 작업을 스프링에서 binding한다고 한다.
@InitBinder
public void init(WebDataBinder wdb) {
// LocalDate로 변환할 에디터를 등록한다면
// wdb.registerCustomEditor(LocalDate,class, new DatePropertyEditor()); → 모든 LocalDate에 대해 동작
// wdb.registerCustomEditor(LocalDate,class, "birthday", new DatePropertyEditor()); → birthday 필드에 대해 동작
wdb.registerCustomEditor(LocalDate.class, "birthday", new DatePropertyEditor());
}
@GetMapping("/member/join")
public void join() {
}
@PostMapping("/member/join")
public String join(Member member) {
// 아이디, 비번, 이메일, 이름, 생일, 레벨을 사용자가 입력한다(레벨은 연습용)
// 문자열 "15"를 입력하면 스프링은 String, Integer, Double로 변환할 수 있다
// 스프링에서 입력할 때 문자열 변환을 담당하는 표준은 PropertyEditor
// 스프링은 문자열, 정수, 실수등의 PropertyEditor를 제공한다
// 그런데 문자열을 날짜로 또는 문자열을 enum으로 변환하는 PropertyEditor는 제공하지 않는다 errorcode400→ 우리가 만들어서 등록하면 된다
// 우선 "2020-01-10"을 LocalDate로 바꾸는 DatePropertyEditor를 만들자
service.join(member);
return "redirect:/member/login";
}
}
DatePropertyEditor.java - 프로퍼티 직접 생성
package com.example.demo.controller.editor;
import java.beans.PropertyEditorSupport;
import java.time.LocalDate;
public class DatePropertyEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
// setAs ctrl+enter
// text 파라미터 : 사용자가 입력한 문자열. 지금같은 경우 "2020-11-20"
String str[] = text.split("-"); // ["2020", "11", "20"]
Integer year = Integer.parseInt(str[0]);
Integer month = Integer.parseInt(str[1]);
Integer day = Integer.parseInt(str[2]);
LocalDate date = LocalDate.of(year, month, day);
// 마지막으로 setValue 사용
setValue(date);
}
}
MemberDao.java
package com.example.demo.dao;
import java.util.Optional;
import org.apache.ibatis.annotations.*;
import com.example.demo.entity.*;
@Mapper
public interface MemberDao {
public Integer save(Member member);
// mybatis 3.5부터 Optional 리턴을 지원
public Optional<Member> findById(String username);
// select * from
public Optional<Member> findByEmail(String email);
// select username from
// public String findUsernameByEmail(String email);
public Integer update(Member member);
public Integer deleteById(String username);
public Boolean existsById(String username);
}
Level.java
package com.example.demo.entity;
public enum Level {
BRONZE, SILVER, GOLD;
}
Member.java
package com.example.demo.entity;
import java.time.*;
import lombok.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Member {
private String username;
private String password;
private String irum;
private String email;
private LocalDate birthday;
private LocalDate joinday;
private Boolean enabled;
private String authority;
private String checkcode;
private Integer count;
private Level levels;
}
MemberService.java
package com.example.demo.service;
import java.lang.reflect.Member;
import java.util.Optional;
import javax.mail.*;
import javax.mail.internet.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.mail.javamail.*;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.example.demo.dao.MemberDao;
import com.example.demo.entity.Level;
import lombok.RequiredArgsConstructor;
// DI하는 방법 : 생성자 주입(@RequiredArgsConstructor), 새터 주입, 필드 주입(@Autowired) → 어떤 DI가 바람직할까? → 롬복을 이용해 생성자 주입을 하자
@RequiredArgsConstructor
@Service
public class MemberService {
private final JavaMailSender javaMailSender;
private final MemberDao memberDao;
private final PasswordEncoder passwordEncoder;
// 메일 보내는 메소드
public void sendMail(String from, String to, String title, String content) throws MessagingException {
// MIME : 이메일의 형식 -> 파일의 형식. 파일의 종류를 MIME type
// html에서 엑셀 문서를 클릭하면 application/excel이라는 MIME 타입이 내 브라우저로 전달된다
// 브라우저는 윈도우에서 엑셀을 찾아서 있으면 엑셀을 실행 -> 엑셀 문서가 열린단
// 엑셀이 없으면 즉 application/excel이란 마임 타입이 알 수 없는 타입이라면 다운로드
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, false, "utf-8");
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(title);
helper.setText(content, true);
javaMailSender.send(message);;
}
// 아이디 사용여부 확인
public Boolean idAvailableCheck(String username) {
// existsById는 null이 발생하지 않는다
return !memberDao.existsById(username);
}
// 아이디 찾기
public String findId(String email) {
// return memberDao.findByEmail(email).getUsername(); → 사용자가 없는 경우 NPE(null pointer exception) 발생할 수 있다
// NPE이 발생하는 지 여부를 판단을 누가 판단할까 → DAO 작성자
// 메소드를 만드는 사람이 Optional 클래스를 이용해 메소드 사용자에게 NPE여부를 가르쳐주자
// Optional이라는 포장상자안에 Member가 들어있다
// Optional을 꺼내는 메소드는 get() → null인 경우 NoSuchElememtException 발생
// memberDao.findByEmail(email).get();
Optional<Member> result = memberDao.findByEmail(email);
if(result.isPresent()==true)
return result.get().getUsername();
return "아이디를 찾지 못했습니다";
}
// 회원가입
public void join(Member member) {
member.setPassword(passwordEncoder.encode(member.getPassword()));
System.out.println(member);
// member.setLevels(Level.BRONZE);
// memberDao.save(member);
}
}
MemberDaoTest.java, MemberServiceTest.java