/login post시 Authentication→ AuthenticationManager로 넘겨준다.
매니저를 구현한 providermanager가 처리해준다.
핕터는 데스탑 매니저 토큰을 만들어서 매니저에게 알려주고 매니저는 이제 직원provider에게 이을 시키고 service라는 외주를 맞기면 details는 이리 저리 뛰어다니면서 정보를 얻은다음에 반대적으로 올라간다.
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.security.web.authentication;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final StringSPRING_SECURITY_FORM_USERNAME_KEY= "username";
public static final StringSPRING_SECURITY_FORM_PASSWORD_KEY= "password";
private String usernameParameter = "username";
private String passwordParameter = "password";
private boolean postOnly = true;
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
String username = this.obtainUsername(request);
String password = this.obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
@Nullable
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(this.passwordParameter);
}
@Nullable
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(this.usernameParameter);
}
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
}
public void setUsernameParameter(String usernameParameter) {
Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
this.usernameParameter = usernameParameter;
}
public void setPasswordParameter(String passwordParameter) {
Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
this.passwordParameter = passwordParameter;
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public final String getUsernameParameter() {
return this.usernameParameter;
}
public final String getPasswordParameter() {
return this.passwordParameter;
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.security.core.userdetails;
public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
package com.studyolle.modules.account;
import com.studyolle.modules.account.form.SignUpForm;
import com.studyolle.infra.config.AppProperties;
import com.studyolle.modules.tag.Tag;
import com.studyolle.modules.zone.Zone;
import com.studyolle.infra.mail.EmailMessage;
import com.studyolle.infra.mail.EmailService;
import com.studyolle.modules.account.form.Notifications;
import com.studyolle.modules.account.form.Profile;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.ModelMapper;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import javax.validation.Valid;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@Slf4j
@Service
/*예외시 롤백역활*/
@Transactional
@RequiredArgsConstructor
public class AccountService implements UserDetailsService {
private final AccountRepository accountRepository;
private final EmailService emailService;
private final PasswordEncoder passwordEncoder;
private final ModelMapper modelMapper;
private final TemplateEngine templateEngine;
private final AppProperties appProperties;
public Account processNewAccount(SignUpForm signUpForm) {
Account newAccount = saveNewAccount(signUpForm);
sendSignUpConfirmEmail(newAccount);
return newAccount;
}
private Account saveNewAccount(@Valid SignUpForm signUpForm) {
signUpForm.setPassword(passwordEncoder.encode(signUpForm.getPassword()));
Account account = modelMapper.map(signUpForm, Account.class);
account.generateEmailCheckToken();
return accountRepository.save(account);
}
public void sendSignUpConfirmEmail(Account newAccount) {
Context context = new Context();
context.setVariable("link", "/check-email-token?token=" + newAccount.getEmailCheckToken() +
"&email=" + newAccount.getEmail());
context.setVariable("nickname", newAccount.getNickname());
context.setVariable("linkName", "이메일 인증하기");
context.setVariable("message", "스터디올래 서비스를 사용하려면 링크를 클릭하세요.");
context.setVariable("host", appProperties.getHost());
String message = templateEngine.process("mail/simple-link", context);
EmailMessage emailMessage = EmailMessage.builder()
.to(newAccount.getEmail())
.subject("스터디올래, 회원 가입 인증")
.message(message)
.build();
//emailService.sendEmail(emailMessage);
}
public void login(Account account) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
new UserAccount(account),
account.getPassword(),
List.of(new SimpleGrantedAuthority("ROLE_USER")));
SecurityContextHolder.getContext().setAuthentication(token);
}
@Transactional(readOnly = true)
@Override
public UserDetails loadUserByUsername(String emailOrNickname) throws UsernameNotFoundException {
Account account = accountRepository.findByEmail(emailOrNickname);
if (account == null) {
account = accountRepository.findByNickname(emailOrNickname);
}
if (account == null) {
throw new UsernameNotFoundException(emailOrNickname);
}
return new UserAccount(account);
}
public void completeSignUp(Account account) {
account.completeSignUp();
login(account);
}
public void updateProfile(Account account, Profile profile) {
modelMapper.map(profile, account);
accountRepository.save(account);
}
public void updatePassword(Account account, String newPassword) {
account.setPassword(passwordEncoder.encode(newPassword));
accountRepository.save(account);
}
public void updateNotifications(Account account, Notifications notifications) {
modelMapper.map(notifications, account);
accountRepository.save(account);
}
public void updateNickname(Account account, String nickname) {
account.setNickname(nickname);
accountRepository.save(account);
login(account);
}
public void sendLoginLink(Account account) {
Context context = new Context();
context.setVariable("link", "/login-by-email?token=" + account.getEmailCheckToken() +
"&email=" + account.getEmail());
context.setVariable("nickname", account.getNickname());
context.setVariable("linkName", "스터디올래 로그인하기");
context.setVariable("message", "로그인 하려면 아래 링크를 클릭하세요.");
context.setVariable("host", appProperties.getHost());
String message = templateEngine.process("mail/simple-link", context);
/*빌더를 쓰는 이유 변수가 많아지면 코드의 가독성이 좋아진다.*/
EmailMessage emailMessage = EmailMessage.builder()
.to(account.getEmail())
.subject("스터디올래, 로그인 링크")
.message(message)
.build();
emailService.sendEmail(emailMessage);
}
public void addTag(Account account, Tag tag) {
Optional<Account> byId = accountRepository.findById(account.getId());
byId.ifPresent(a -> a.getTags().add(tag));
}
public Set<Tag> getTags(Account account) {
Optional<Account> byId = accountRepository.findById(account.getId());
return byId.orElseThrow().getTags();
}
public void removeTag(Account account, Tag tag) {
Optional<Account> byId = accountRepository.findById(account.getId());
byId.ifPresent(a -> a.getTags().remove(tag));
}
public Set<Zone> getZones(Account account) {
Optional<Account> byId = accountRepository.findById(account.getId());
return byId.orElseThrow().getZones();
}
public void addZone(Account account, Zone zone) {
Optional<Account> byId = accountRepository.findById(account.getId());
byId.ifPresent(a -> a.getZones().add(zone));
}
public void removeZone(Account account, Zone zone) {
Optional<Account> byId = accountRepository.findById(account.getId());
byId.ifPresent(a -> a.getZones().remove(zone));
}
public Account getAccount(String nickname) {
Account account = accountRepository.findByNickname(nickname);
if (account == null) {
throw new IllegalArgumentException(nickname + "에 해당하는 사용자가 없습니다.");
}
return account;
}
}