login 구현

화면부터 구성해보자

  • /resources/templates/auth/login
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>login</title>
</head>
<body>
<form method="post" th:action="@{/auth/login/process}">
  <div style="padding: 30px"></div>
  <div style="padding: 15px">
    <input placeholder="username" id="username" name="username" type="text"/>
    <input placeholder="password" id="password" name="password" type="password"/>
    <button type="submit">login</button>
    <!--    로그인 실패 경고-->
    <div th:if="${error}" class="alert bg--error">
      <div class="alert__body">
        <span th:text="${error}" class="text-danger" style="color: #ff1034;"></span>
      </div>
    </div>
  </div>
  <div style="padding: 15px">
    <a th:href="@{/auth/find-account/username}">Forgot your Username or Password?</a>
    <span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
    <a th:href="@{/auth/register}">Sign up</a>
  </div>
</form>

</body>
</html>

 

  • AuthController : 화면과 연결
@Controller
@RequiredArgsConstructor
@RequestMapping("/auth")
public class AuthController {

  @NonNull
  private final UserService userService;
  @NonNull
  private final SmartValidator validator;

  @RequestMapping("/login")
  public String login() {
    return "auth/login";
  }
  }

 

  • UserService
@Service
@RequiredArgsConstructor
public class UserService implements UserDetailsService {

  @NonNull
  private final BCryptPasswordEncoder bCryptPasswordEncoder;
  @NonNull
  private final UserRepository userRepository;

  public Optional<User> findById(Integer id) {
    return userRepository.findById(id);
  }

  public Page<User> findAllByFilter(Pageable pageable, UserFilter filter) {
    return userRepository.findAllByFilter(pageable, filter);
  }

  public User save(User user) throws UserPasswordValidationException, DataNotFoundException {
    if (user.hasPasswordChanged()) {
      if (PasswordUtil.isValidPassword(user.getPassword())) {
        String encodedPassword = bCryptPasswordEncoder.encode(user.getPassword());
        user.setPassword(encodedPassword);
        user.setLastPasswordChangeDate(LocalDate.now());
      } else {
        throw new UserPasswordValidationException();
      }
    } else {
      user.setPassword(userRepository.findPasswordByUser(user));
    }
    return userRepository.save(user);
  }

  @Transactional
  public int removeByIdIn(List<Integer> ids) {
    return userRepository.removeByIdIn(ids);
  }

  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    Optional<User> user = userRepository.findByUsername(username);
    if (!user.isPresent()) {
      throw new UsernameNotFoundException("일치하는 사용자를 찾을 수 없습니다.");
    }
    return user.get();
  }

  public boolean existsByUsername(String username) {
    return userRepository.existsByUsername(username);
  }

  public boolean existsByName(String name) {
    return userRepository.existsByName(name);
  }

  public Page<User> findAllByIdIn(List<Integer> ids, Pageable pageable) {
    return userRepository.findAllByIdIn(ids, pageable);
  }
}

❗ 나중에 활용할 함수들 ❗

 

  • UserRepository
//UserRepository
@Repository
public interface UserRepository extends JpaRepository<User, Integer>, CustomUserRepository {

  Optional<User> findByUsername(String username);

  boolean existsByUsername(String username);

  boolean existsByName(String name);

  @Query("SELECT p.password FROM User p WHERE p.id = :#{#user.id}")
  String findPasswordByUser(@Param("user") User user);

  @Modifying
  @Query("UPDATE User p SET p.removed = true, p.username = CONCAT(p.username, '-removed') WHERE p.id IN(:ids)")
  int removeByIdIn(@Param("ids") List<Integer> ids);

  Page<User> findAllByIdIn(List<Integer> ids, Pageable pageable);
}

//CustomUserRepository
@NoRepositoryBean
public interface CustomUserRepository {

  Page<User> findAllByFilter(Pageable pageable, UserFilter filter);

}

//UserRepositoryImpl
public class UserRepositoryImpl extends QuerydslRepositorySupport implements CustomUserRepository {

  private QUser user = QUser.user;

  public UserRepositoryImpl() {
    super(User.class);
  }

  @Override
  public Page<User> findAllByFilter(Pageable pageable, UserFilter filter) {
    BooleanBuilder builder = new BooleanBuilder();

    builder.and(user.removed.isFalse());

    if (filter.getFRole() != null) {
      builder.and(user.role.eq(filter.getFRole()));
    }

    if (!StringUtils.isBlank(filter.getFName())) {
      builder.and(user.name.containsIgnoreCase(filter.getFName()));
    }

    if (!StringUtils.isBlank(filter.getFUsername())) {
      builder.and(user.username.containsIgnoreCase(filter.getFUsername()));
    }

    if (filter.getFLocked() != null) {
      builder.and(user.locked.eq(filter.getFLocked()));
    }

    final JPQLQuery<User> query = from(user).where(builder);
    final List<User> result = getQuerydsl().applyPagination(pageable, query).fetch();
    return new PageImpl<>(result, pageable, query.fetchCount());
  }
}

 

  • UserFilter
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class UserFilter extends QueryStringSupport {

  private Role fRole;

  private String fUsername;

  private String fName;

  private Boolean fLocked;

}

 

    • QueryStringSupport
@Getter
@Setter
public class QueryStringSupport {

  protected String sort;
  protected Integer page;

  public String getQueryString() {
    try {
      List<String> queries = new ArrayList<>();
      List<Field> fieldList = FieldUtils.getAllFieldsList(this.getClass());
      for (Field f : fieldList) {
        String key = f.getName();
        Object value = FieldUtils.readField(f, this, true);

        if (value == null) {
          // querys.add(key + "=");
          continue;
        }

        if (value instanceof Collection) {
          Collection col = (Collection) value;
          for (Object o : col) {
            queries.add(key + "=" + new URLCodec().encode(o.toString()));
          }
        } else {
          queries.add(key + "=" + new URLCodec().encode(value.toString()));
        }
      }

      return Strings.join(queries, '&');
    } catch (IllegalAccessException | EncoderException e) {
      return "";
    }
  }

  public void fromQueryString(String queryString) {
    List<Field> fieldList = FieldUtils.getAllFieldsList(this.getClass());
    List<NameValuePair> nameValuePairs = URLEncodedUtils
        .parse(queryString, Charset.forName("UTF-8"));

    for (Field f : fieldList) {
      String fieldName = f.getName();

      for (NameValuePair p : nameValuePairs) {
        String paramName = p.getName();

        if (fieldName.equalsIgnoreCase(paramName)) {
          try {
            FieldUtils.writeField(f, this, p.getValue());
          } catch (IllegalAccessException e) {
            //DO Nothing
          }
          break;
        }
      }
    }
  }
}

 

위와 같이 코드를 상황에 맞게 설정하고 DB에 사용자를 추가해서 login해보기!

 

 

logout구현

<html xmlns:sec="http://www.thymeleaf.org/extras/spring-security">

<a sec:authorize="isAnonymous()" th:href="@{/auth/login}" style="padding: 15px">login</a>
<a sec:authorize="isAuthenticated()" th:href="@{/auth/logout}" style="padding: 15px">logout</a>

isAnonymous() : 익명의 사용자

isAuthenticated() : 인증한 사용자

 

위와 같이 설정하면 로그인 하기 전에는 login a태그를 로그인에 성공하고 나면 logout a태그를 보여준다.

 

❗ 다음 포스팅은 register 관련 ❗

+ Recent posts