66日目:Spring+MyBasis(メールの送信、IDの使用の確認、IDの検索、会員登録サービス、Propertyの直接作成)


2022.02.17.Thur.


復習する


共通


ちょっと違うspringboot start



セキュリティレベルの低いアプリケーションを有効にする方法


Googleアカウント管理→セキュリティ→セキュリティレベルの低いアプリケーションの使用を許可

sql.txtの作成


tableを作成し、cmdウィンドウにコピーして貼り付けます.

設定のキャプチャとファイルの追加


スナップの設定
pom.xml
mapper.xmlの変更
application.properties
ファイルの追加
log4jdbc.log4j2.properties
logback-spring.xml

DIメソッド


注入作成者(@RequiredArgConstructor)、新しいデータの注入、フィールドの注入(@Autowired)
どのようなDIが良いですか?
ロムフォードを利用して生成者に注入する.

順序どおりに勤務する.


com.example.demo


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();
	}
}
  • セキュリティ方法と継承:@Configuration,@EnableWebSecurity,@EnableGlobalMethodSecurity(PrePostEnabled=true,SecureEnabled=true),Public Class SecurityConfig extends WebSecurityConfigurator Adapter
    @Autowired
    private PasswordEncoder passwordEncoder;
  • protected void configure(HttpSecurity http) throws Exception {
    http:WebSecurityConfigurator Adapterが提供する構成(HTTP)のプリファレンス
  • .authorizeHttpRequests():ユーザーが認証されていない場合、springセキュリティフィルタはリクエストを取得し、ログインページにリダイレクトします.
  • .antMatchers():antMatcherへのパスはAntスタイルのワイルドカードを使用できます.指定パス
  • .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();
    	}
    }
  • @MapperScan(「com.example.demo.dao」):マッパーを1つずつ登録するのではなく、パッケージパスを指定します.以下のすべてのインタフェースをマッパーとして使用できます.
  • com.example.demo.controller


    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";
    	}
    }
  • @RequiredArgsConstructor:注入構造関数
  • @InitBinder:カスタムデータ型変換の処理
  • @InitBinder....registerCustomEditor():要求パラメータをバインドする前に自動的に呼び出され、registerCustomEditorメソッドを使用してWebDataBinderに独自に定義したPropertyEditorインプリメンテーションクラスを登録できます.
  • com.example.demo.controller.editor


    DatePropertyEditor.JAva-Propertyの直接作成
    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);
    	}
    }

    com.example.demo.dao


    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);
    
    }

    com.example.demo.entity


    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;
    }
    

    com.example.demo.service


    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);
    	}
    }
  • throws:メソッドまたはジェネレータを実行するときに、例外を宣言するときに使用されるキーワードは、自分で例外を処理するのではなく、自分を呼び出すメソッドに責任を転嫁します.
  • throws EllegalArgumentException:メソッドに不適切または不適切な(不適切な)パラメータを渡すときに例外処理を行います.このエラーは呼び出し元で処理する必要があります.
  • Test


    MemberDaoTest.java, MemberServiceTest.java

    ファイルをsvnにインポート(混同...)