단의 개발 블로그

Security 사용하기 본문

Web/Spring

Security 사용하기

danso 2024. 10. 8. 23:30

사용자 등록을 추가한다.

controller/RegistrationController

@Controller
@RequestMapping("/register")
@RequiredArgsConstructor
public class RegistrationController {

  private final UserRepository userRepository;

  private final PasswordEncoder passwordEncoder;

  @GetMapping
  public String registerForm() {
    return "registration";
  }

  @PostMapping
  public String processRegistration(RegistrationForm form) {
    userRepository.save(form.toUser(passwordEncoder));
    return "redirect:/login";
  }
}

templates/registration.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="EUC-KR">
  <title>Taco Cloud</title>
</head>

<body>
<h1>Register</h1>
<img th:src="@{/images/TacoCloud.png}" />
<form method="POST" th:action="@{/register}" id="registerForm">
  <label for="username">Username: </label>
  <input type="text" name="username" /><br />

  <label for="password">Password: </label>
  <input type="password" name="password" /><br />

  <label for="confirm">Confirm password: </label>
  <input type="password" name="confirm" /><br />

  <label for="fullname">Full name: </label>
  <input type="text" name="fullname" /><br />

  <label for="street">Street: </label>
  <input type="text" name="street" /><br />

  <label for="city">City: </label>
  <input type="text" name="city" /><br />

  <label for="state">State: </label>
  <input type="text" name="state" /><br />

  <label for="zip">Zip: </label>
  <input type="text" name="zip" /><br />

  <label for="phone">Phone: </label>
  <input type="text" name="phone" /><br />

  <input type="submit" value="Register" />
</form>
</body>
</html>

templates/login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="EUC-KR">
  <title>Taco Cloud</title>
</head>

<body>
<h1>Login</h1>
<img th:src="@{/images/TacoCloud.png}" />

<div th:if="${error}">Unable to login. Check your username and
  password.</div>

<p>
  New here? Click <a th:href="@{/register}">here</a> to register.
</p>

<!-- tag::thAction[] -->
<form method="POST" th:action="@{/login}" id="loginForm">
  <!-- end::thAction[] -->
  <label for="username">Username: </label>
  <input type="text" name="username" id="username" /><br/>

  <label for="password">Password: </label>
  <input type="password" name="password" id="password" /><br/>

  <input type="submit" value="Login"/>
</form>
</body>
</html>

config/WebConfig.java에 해당 코드 추가

...
registry.addViewController("/login").setViewName("login");
...

 

SecurityConfig 필터 체인 변경

    http
        .authorizeHttpRequests(au ->
        {
          au
              .requestMatchers("/design", "/orders").hasRole("USER")
              .requestMatchers("/", "/**").permitAll();
        })
        .formLogin(login ->{
          login
              .loginPage("/login")
              .defaultSuccessUrl("/");
        })
        .logout(logout ->{
          logout.logoutSuccessUrl("/");
        });
  • 사용자가 로그인 할 페이지를 지정한다.
  • 해당 옵션으로 인증을 어디서 처리할 것인지 정할 수 있으며, 프론트에서 전달받은 사용자의 키 값도 변경할 수 있다!
  • logout도 설정하여 인증된 유저가 로그이웃되도록 처리한다.

인증된 사용자가 주문을 할 경우 order 객체 최초 생성시 해당 주문을 하는 사용자의 이름과 주소 폼에 미리 넣을 수 있다면 좋다. 따라서 Order 엔티티에 아래 코드를 추가한다.

@ManyToOne
private User user;

 

OrderController에서 주문을 처리하는 processOrder() 메소드가 주문을 저장하는 일을 수행 하도록 설정한다. 인증된 사용자가 누구인지 받는 방법은 여러가지이지만 주로 아래 네가지 방식을 사용한다.

  • Pricipal 객체를 컨트롤러 메소드에 주입
  • Authentication 객체를 컨트롤러 메소드에 주입
  • SecurityContextHolder를 사용해서 보안 컨텍스트를 얻어서 저장
  • @AuthenticationPrincipal 어노테이션 메소드에 지정

여기서는 어노테이션 방식으로 사용한다. OrderController의 processOrder 메소드 수정

@PostMapping
public String processOrder(
    @Valid Order order, Errors errors,
    SessionStatus sessionStatus,
    @AuthenticationPrincipal User user
) {
  if (errors.hasErrors()){
    return "orderForm";
  }
  order.setUser(user);
  
  orderRepository.save(order);
  sessionStatus.setComplete();
  return "redirect:/";
}

 

사용자가 인증된 유저의 정보를 미리 받아올 수 있도록 수정해야 한다.

OrderForm.html 

<form method="POST" th:action="@{/logout}" id="logoutForm">
    <input type="submit" value="Logout"/>
</form>

<form method="POST" th:action="@{/orders}" th:object="${order}" id="orderForm">
    <h1>Order your taco creations!</h1>

    <img th:src="@{/images/TacoCloud.png}" /> 

    <h3>Your tacos in this order:</h3>

    <a th:href="@{/design}"
        id="another">Design another taco</a><br />

    <ul>
        <li th:each="taco : ${order.tacos}">
        <span th:text="${taco.name}">taco name</span></li>
    </ul>

    <div th:if="${#fields.hasErrors()}">
        <span class="validationError"> Please correct the problems
            below and resubmit. </span>
    </div>

    <h3>Deliver my taco masterpieces to...</h3>

    <label for=" deliveryName">Name: </label> 
    <input type="text" th:field="*{deliveryName}" /> 
    <span class="validationError"
        th:if="${#fields.hasErrors('deliveryName')}"
        th:errors="*{deliveryName}">Name Error</span>
    <br /> 

    <label for="deliveryStreet">Street address: </label> 
    <input type="text" th:field="*{deliveryStreet}" /> 
    <span class="validationError"
        th:if="${#fields.hasErrors('deliveryStreet')}"
        th:errors="*{deliveryStreet}">Street Error</span>
    <br /> 

    <label for="deliveryCity">City: </label> 
    <input type="text" th:field="*{deliveryCity}" /> 
    <span class="validationError"
        th:if="${#fields.hasErrors('deliveryCity')}"
        th:errors="*{deliveryCity}">City Error</span>
    <br /> 

    <label for="deliveryState">State: </label> 
    <input type="text" th:field="*{deliveryState}" />
    <span class="validationError"
        th:if="${#fields.hasErrors('deliveryState')}"
        th:errors="*{deliveryState}">State Error</span>
    <br /> 

    <label for="deliveryZip">Zip code: </label> 
    <input type="text" th:field="*{deliveryZip}" /> 
    <span class="validationError"
        th:if="${#fields.hasErrors('deliveryZip')}"
        th:errors="*{deliveryZip}">Zip Error</span>
    <br />

    <h3>Here's how I'll pay...</h3>
    <label for="ccNumber">Credit Card #: </label> 
    <input type="text" th:field="*{ccNumber}" /> 
    <span class="validationError"
        th:if="${#fields.hasErrors('ccNumber')}"
        th:errors="*{ccNumber}">CC Num Error</span>
    <br /> 

    <label for="ccExpiration">Expiration: </label> 
    <input type="text" th:field="*{ccExpiration}" /> 
    <span class="validationError"
        th:if="${#fields.hasErrors('ccExpiration')}"
        th:errors="*{ccExpiration}">CC Num Error</span>
    <br /> 

    <label for="ccCVV">CVV: </label> 
    <input type="text" th:field="*{ccCVV}" /> 
    <span class="validationError"
        th:if="${#fields.hasErrors('ccCVV')}"
        th:errors="*{ccCVV}">CC Num Error</span>
    <br />

    <input type="submit" value="Submit order" />

DesignTacoController showDesignForm() 메소드 변경

@GetMapping
public String showDesignForm(
    Model model,
    Principal principal
)
{
  List<Ingredient> ingredients = new ArrayList<>();
  ingredientRepository.findAll().forEach(ingredients::add);

  Type[] types = Ingredient.Type.values();
  for (Type type : types) {
    model.addAttribute(type.toString().toLowerCase(),
        filterByType(ingredients, type));
  }

  String username = principal.getName();
  User user = userRepository.findByUsername(username);
  model.addAttribute("user", user);

  return "design";
}

 

로그인 한 유저가 언제든지 로그아웃을 할 수 있도록 알맞게 수정한다! (이건 직접해보기)

 

서비스를 실행하고 taco 등록을 클릭한다. 만약 인증된 사용자가 아니면 로그인을 하도록 페이지가 이동되고, 인증된 사용자일 경우 타코 주문이 가능해진다. 

인증된 유저가 로그아웃을 클릭하면 시큐리티에서 사라지고, 타코 주문 화면으로 이동시 로그인 페이지로 리다이렉트 된다.

 

참고

https://www.aladin.co.kr/m/mproduct.aspx?ItemId=14838368&srsltid=AfmBOorCcfwGfxizbzDr1znvMo6rjYHskM1Lki0CBoBPojpaYpBksMsP

 

'Web > Spring' 카테고리의 다른 글

소셜로그인  (0) 2024.11.07
Spring 구성속성  (0) 2024.11.01
Security  (0) 2024.10.08
JPA  (1) 2024.09.05
데이터 베이스  (0) 2024.09.04