참조문서: https://codevang.tistory.com/ & https://bin-repository.tistory.com/128?category=879445
//컨트롤러
@PostMapping("join.do")
public ModelAndView join(Member member) {
int joinCode = service.join(member);
if(joinCode == JOIN_F_EMAIL_EXIST) {
//log.info(joinCode);
return new ModelAndView("member/join","joinCode", joinCode); // 회원가입 실패 메세지
}else if(joinCode == JOIN_SUCCESS){
return new ModelAndView("member/login","joinCode", joinCode); // 회원가입 성공, 이메일 인증 요구 메세지
}
return null;
}
@GetMapping("joinConfirm")
public ModelAndView joinConfirm(Member member) {
int joinCode = service.checkMailAuth(member);
return new ModelAndView("member/login","joinCode",joinCode); // 인증 성공, 또는 실패
}
@Service
@AllArgsConstructor
public class MemberServiceImpl implements MemberService {
private MemberMapper mapperM;
private BCryptPasswordEncoder bcryptPwdEncoder;
private JavaMailSenderImpl mailSender;
@Override
public int join(Member member) {
try{
String encodedPwd = bcryptPwdEncoder.encode(member.getM_pwd());
member.setM_pwd(encodedPwd);
String emailAuth = sendAuthMail(member.getM_email()); // 인증메일 발송
member.setM_mailAuth(emailAuth);
mapperM.insertMember(member);
return JOIN_SUCCESS;
}catch(DataAccessException dae) {
System.out.println(dae);
return JOIN_F_EMAIL_EXIST;
}
}
private String sendAuthMail(String email) {
//6자리 난수 인증번호 생성
String authKey = getKey(6);
//인증메일 보내기
try {
MailUtils sendMail = new MailUtils(mailSender);
sendMail.setSubject("회원가입 이메일 인증");
sendMail.setText(new StringBuffer().append("<h1>[이메일 인증]</h1>")
.append("<p>아래 링크를 클릭하시면 이메일 인증이 완료됩니다.</p>")
.append("<a href='http://localhost:8080/member/joinConfirm?m_email=")
.append(email)
.append("&m_mailAuth=")
.append(authKey)
.append("' target='_blenk'>이메일 인증 확인</a>")
.toString());
sendMail.setFrom("accForHibooks@gmail.com", "관리자");
sendMail.setTo(email);
sendMail.send();
} catch (MessagingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return authKey;
}
@Override
public int checkMailAuth(Member member) { // 이메일 인증시 계정 활성화
Member memberChecked = mapperM.selectMemberInfo(member);
String inputMailAuth = member.getM_mailAuth();
String getMailAuth = memberChecked.getM_mailAuth();
if(inputMailAuth.equals(getMailAuth)) {
mapperM.updateEnabled(memberChecked); // enabled 컬럼을 0 -> 1로 바꿈
return MAIL_AUTH_SUCCESS;
}else {
return MAIL_AUTH_FAIL;
}
}
.............
}
@SuppressWarnings("serial")
public class MemberDetails implements UserDetails {
private Member member;
public MemberDetails(Member member) {
this.member = member;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
ArrayList<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
authList.add(new SimpleGrantedAuthority(member.getM_auth()));
return authList;
}
@Override
public String getPassword() {
return member.getM_pwd();
}
@Override
public String getUsername() {
return member.getM_email();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return member.getEnabled()==1?true:false;
}
// 회원정보를 불러와야할 경우
public Member getMember() {
member.setM_pwd(null);
return member;
}
public void setMember(Member member) {
this.member = member;
}
}
public class MemberDetailsService implements UserDetailsService {
@Autowired
private MemberMapper mapper;
@Override
public UserDetails loadUserByUsername(String m_email) throws UsernameNotFoundException {
Member member = new Member();
member.setM_email(m_email);
member = mapper.selectMemberInfo(member);
if(member == null) throw new UsernameNotFoundException("없는 이메일");
MemberDetails user = new MemberDetails(member);
return user;
}
}
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring.security.version}</version>
</dependency>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="bit.hibooks.security"></context:component-scan>
<!-- 암호화 객체 생성 -->
<bean id="bcryptPwdEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
<!-- 로그인 시 시큐리티에서 사용하는 서비스 객체(userDetailsService 클래스 커스터마이징) -->
<bean id="memberDetailsService" class="bit.hibooks.security.MemberDetailsService"></bean>
<!-- 로그인 실패시 시큐리티에서 사용하는 객체 -->
<bean id="loginFailHandler" class="bit.hibooks.security.LoginFailHandler"></bean>
<!-- csrf 제외 url 설정 -->
<!-- <bean id="csrfSecurityRequestMatcher" class="bit.hibooks.security.CsrfSecurityRequestMatcher"/> -->
<!-- 시큐리티 설정 -->
<security:http>
<security:csrf disabled="true"/>
<security:intercept-url pattern="/" access="permitAll"/>
<security:intercept-url pattern="/member/moveMyInfo.do" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/wishList/*" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/purchase/checkout.do" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/purchase/placeorder.do" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/purchase/orderComplete.do" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/boardq/write.do" access="hasRole('ROLE_USER')"/>
<security:form-login login-page="/member/login.do"
username-parameter="m_email"
password-parameter="m_pwd"
login-processing-url="/member/login.do"
always-use-default-target="true"
default-target-url="/"
authentication-failure-handler-ref="loginFailHandler"/>
<security:access-denied-handler error-page="/"/>
<security:logout logout-url="/member/logout.do"
logout-success-url="/" />
<!-- max session setting -->
<security:session-management invalid-session-url="/">
<security:concurrency-control max-sessions="1"
expired-url="/"
error-if-maximum-exceeded="true" />
</security:session-management>
<!-- remember-me -->
<security:remember-me user-service-ref="memberDetailsService" remember-me-parameter="remember-me"
token-validity-seconds="600000" />
</security:http>
<!-- get UserInfo by MyBatis from DB -->
<!-- 임의의 계정 등록(관리자), 데이터베이스에 저장된 정보 참조 -->
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="admin" password="admin" authorities="ROLE_ADMIN" />
</security:user-service>
</security:authentication-provider>
<security:authentication-provider user-service-ref="memberDetailsService">
<security:password-encoder hash="bcrypt"></security:password-encoder>
</security:authentication-provider>
</security:authentication-manager>
</beans>