๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๋ฐฑ์—”๋“œ/spring&springboot

[๋‚ด์ผ๋ฐฐ์›€์บ ํ”„_Spring Boot ์ˆ™๋ จ์ฃผ์ฐจ] ์‚ฌ์šฉ์ž ๊ด€๋ฆฌํ•˜๊ธฐ(ํšŒ์›๊ฐ€์ž…, ๋กœ๊ทธ์ธ ๊ตฌํ˜„ with JWT, ํ•„ํ„ฐ)

by alswlfl 2026. 6. 29.

ํšŒ์›๊ฐ€์ž… ๊ตฌํ˜„

1. ํ”„๋กœ์ ํŠธ ์„ค์ • ์ถ”๊ฐ€

// build.gradle ๋‚ด JPA, MySQL ์ถ”๊ฐ€
// JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// MySQL
runtimeOnly 'com.mysql:mysql-connector-j'
// application.properties ๋‚ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ •๋ณด ์ถ”๊ฐ€
spring.datasource.url=jdbc:mysql://localhost:3306/auth
spring.datasource.username=root
spring.datasource.password={๋น„๋ฐ€๋ฒˆํ˜ธ}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

 

2. auth ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒ์„ฑ ํ›„ ์ธํ…”๋ฆฌ์ œ์ด ์—ฐ๋™

CREATE DATABASE auth;

 

3. HTML, CSS, JS ์ฝ”๋“œ ์ถ”๊ฐ€

src > main > resources > templates ์•„๋ž˜ HTML ์ฝ”๋“œ ์ถ”๊ฐ€(index.html, login.html, signup.html)

src > main > resources > static ๋‚ด css, js ํด๋” ์ถ”๊ฐ€ ํ›„ style.css๊ณผ basic.js ์ƒ์„ฑ

 

4. ํŽ˜์ด์ง€ Controller ์ƒ์„ฑ

  • HomeController
package com.sparta.springauth.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

    @GetMapping("/")
    public String home(Model model) {
        model.addAttribute("username", "username");
        return "index"; // templates > index.html์ธ ๋ฉ”์ธ ํŽ˜์ด์ง€ ๋ฐ˜ํ™˜
    }
}
  • UserController
package com.sparta.springauth.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/api")
public class UserController {

    @GetMapping("/user/login-page")
    public String loginPage() {
        return "login"; // ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ๋ฐ˜ํ™˜
    }

    @GetMapping("/user/signup")
    public String signupPage() {
        return "signup"; // ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ ๋ฐ˜ํ™˜
    }
}

์‹คํ–‰ ๊ฒฐ๊ณผ ํ™”๋ฉด

5. User ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑ

package com.sparta.springauth.entity;

public enum UserRoleEnum {
    USER(Authority.USER),  // ์‚ฌ์šฉ์ž ๊ถŒํ•œ
    ADMIN(Authority.ADMIN);  // ๊ด€๋ฆฌ์ž ๊ถŒํ•œ

    private final String authority;

    UserRoleEnum(String authority) {
        this.authority = authority;
    }

    public String getAuthority() {
        return this.authority;
    }

    public static class Authority {
        public static final String USER = "ROLE_USER";
        public static final String ADMIN = "ROLE_ADMIN";
    }
}
package com.sparta.springauth.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private String username;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false)
    @Enumerated(value = EnumType.STRING)
    private UserRoleEnum role;
}
  • @Enumerated ์–ด๋…ธํ…Œ์ด์…˜์€ ๋ฐ์ดํ„ฐ enum ํƒ€์ž…์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปฌ๋Ÿผ์— ์ €์žฅํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜
    • ์˜ต์…˜์œผ๋กœ EnumType.STRING์„ ์‚ฌ์šฉํ•˜๋ฉด enum์˜ ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•จ

ํŒจ์Šค์›Œ๋“œ ์•”ํ˜ธํ™”

ํšŒ์› ๋“ฑ๋ก ์‹œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๋ฌธ์ž ๊ทธ๋Œ€๋กœ DB์— ๋“ฑ๋กํ•˜๋ฉด ์•ˆ๋จ! '์ •๋ณดํ†ต์‹ ๋ง๋ฒ•, ๊ฐœ์ธ์ •๋ณด๋ณดํ˜ธ๋ฒ•'์— ์˜ํ•ด ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™”(Encryption)๊ฐ€ ์˜๋ฌด์ž„

KISA ๊ฐœ์ธ์ •๋ณด ์ข…๋ฅ˜์™€ ์ ์šฉ ๊ฐ€๋Šฅ ์•”ํ˜ธ๊ธฐ์ˆ 

  • 'ํ‰๋ฌธ → (์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜) → ์•”ํ˜ธ๋ฌธ'๋กœ ์•”ํ˜ธํ™” ํ›„ ํŒจ์Šค์›Œ๋“œ ์ €์žฅ ํ•„์š”ํ•จ
  • ํ•ด์ปค๊ฐ€ DB์— ์žˆ๋Š” ํŒจ์Šค์›Œ๋“œ ์ •๋ณด๋ฅผ ๊ฐˆ์ทจํ•˜๋”๋ผ๋„ ์‹ค์ œ ์•”ํ˜ธ๋ฅผ ์•Œ ์ˆ˜ ์—†๊ณ , ๋ณตํ˜ธํ™”๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•œ ๋‹จ๋ฐฉํ–ฅ ์•”ํ˜ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์‚ฌ์šฉ์ด ํ•„์š”ํ•จ

์–‘๋ฐฉํ–ฅ ์•”ํ˜ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜

  • ์•”ํ˜ธํ™”: ํ‰๋ฌธ → (์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜) → ์•”ํ˜ธ๋ฌธ
  • ๋ณตํ˜ธํ™”: ์•”ํ˜ธ๋ฌธ → (์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜) → ํ‰๋ฌธ
  • ์ฆ‰, ์•”ํ˜ธํ™”์™€ ๋ณตํ˜ธํ™”๊ฐ€ ๋ชจ๋‘ ๊ฐ€๋Šฅํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜

๋‹จ๋ฐฉํ–ฅ ์•”ํ˜ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜

  • ์•”ํ˜ธํ™”: ํ‰๋ฌธ → (์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜) → ์•”ํ˜ธ๋ฌธ
  • ๋ณตํ˜ธํ™”: ์•”ํ˜ธ๋ฌธ→ (์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜)→ ํ‰๋ฌธ โ–บ ๋ถˆ๊ฐ€๋Šฅ
  • ์ฆ‰, ์•”ํ˜ธํ™”๋Š” ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๋ณตํ˜ธํ™”๋Š” ๋ถˆ๊ฐ€๋Šฅํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜
โ“ ์‚ฌ์šฉ์ž๋Š” ๋กœ๊ทธ์ธํ•  ๋•Œ ์•”ํ˜ธํ™”๋œ ํŒจ์Šค์›Œ๋“œ๋ฅผ ๊ธฐ์–ตํ•ด์•ผ ํ• ๊นŒ?

Spring Security๋ผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ boolean matches(CharSequence rawPassword, String encodedPassword);๋ฅผ ์ œ๊ณตํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•”ํ˜ธํ™”ํ•ด ์ €์žฅ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ๋น„๊ตํ•ด ์ผ์น˜์—ฌ๋ถ€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ

1. ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ์„ ์œ„ํ•ด "์•„์ด๋””, ํŒจ์Šค์›Œ๋“œ (ํ‰๋ฌธ)" ์ž…๋ ฅํ•ด ์„œ๋ฒ„์— ๋กœ๊ทธ์ธ ์š”์ฒญ
2. ์„œ๋ฒ„์—์„œ ํŒจ์Šค์›Œ๋“œ (ํ‰๋ฌธ)์„ ์•”ํ˜ธํ™” ์ˆ˜ํ–‰: ํ‰๋ฌธ → (์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜) → ์•”ํ˜ธ๋ฌธ
3. DB์— ์ €์žฅ๋œ "์•„์ด๋””, ํŒจ์Šค์›Œ๋“œ (์•”ํ˜ธ๋ฌธ)"์™€ ์ผ์น˜ ์—ฌ๋ถ€ ํ™•์ธ

ํšŒ์›๊ฐ€์ž… API ๊ตฌํ˜„

Name Method URL ์„ค๋ช…
ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ GET /api/user/signup ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ ํ˜ธ์ถœ
ํšŒ์›๊ฐ€์ž… POST /api/user/signup ํšŒ์›๊ฐ€์ž…

 

6. ํšŒ์›๊ฐ€์ž… API ๋งŒ๋“ค๊ธฐ

6-1) RequestDto ์ƒ์„ฑ

package com.sparta.springauth.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class SignupRequestDto {
    private String username;
    private String password;
    private String email;
    private boolean admin = false;
    private String adminToken = "";
}

 

6-2) ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฒ˜๋ฆฌํ•  Service ๋งŒ๋“ค๊ธฐ

package com.sparta.springauth.service;

import com.sparta.springauth.dto.SignupRequestDto;
import com.sparta.springauth.entity.User;
import com.sparta.springauth.entity.UserRoleEnum;
import com.sparta.springauth.repository.UserRepository;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class UserService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }

    // ADMIN_TOKEN
    private final String ADMIN_TOKEN = "AAABnvxRVklrnYxKZ0aHgTBcXukeZygoC";

    public void signup(SignupRequestDto requestDto) {
        String username = requestDto.getUsername();
        String password = passwordEncoder.encode(requestDto.getPassword());

        // ํšŒ์› ์ค‘๋ณต ํ™•์ธ
        Optional<User> checkUsername = userRepository.findByUsername(username);
        if (checkUsername.isPresent()) {
            throw new IllegalArgumentException("์ค‘๋ณต๋œ ์‚ฌ์šฉ์ž๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.");
        }

        // email ์ค‘๋ณตํ™•์ธ
        String email = requestDto.getEmail();
        Optional<User> checkEmail = userRepository.findByEmail(email);
        if (checkEmail.isPresent()) {
            throw new IllegalArgumentException("์ค‘๋ณต๋œ Email ์ž…๋‹ˆ๋‹ค.");
        }

        // ์‚ฌ์šฉ์ž ROLE ํ™•์ธ
        UserRoleEnum role = UserRoleEnum.USER;
        if (requestDto.isAdmin()) {
            if (!ADMIN_TOKEN.equals(requestDto.getAdminToken())) {
                throw new IllegalArgumentException("๊ด€๋ฆฌ์ž ์•”ํ˜ธ๊ฐ€ ํ‹€๋ ค ๋“ฑ๋ก์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.");
            }
            role = UserRoleEnum.ADMIN;
        }

        // ์‚ฌ์šฉ์ž ๋“ฑ๋ก
        User user = new User(username, password, email, role);
        userRepository.save(user);
    }
}
  • ADMIN_TOKEN์€ ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž์ธ์ง€ ๊ด€๋ฆฌ์ž์ธ์ง€ ๊ตฌ๋ถ„ํ•  ๋•Œ ์‚ฌ์šฉ
  • ์‹ค์ œ๋กœ ํ˜„์—…์—์„œ๋Š” ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ์ค„ ๋•Œ ์ €๋Ÿฐ์‹์œผ๋กœ ์ฃผ์ง€ ์•Š๊ณ , ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€๋ฅผ ๋”ฐ๋กœ ๊ตฌํ˜„ํ•˜๊ฑฐ๋‚˜ ์Šน์ธ์ž์— ์˜ํ•ด์„œ ๊ฒฐ์žฌํ•˜๋Š” ๊ณผ์ •์„ ๊ตฌํ˜„ํ•จ

6-3) Repository ์ƒ์„ฑ

package com.sparta.springauth.repository;

import com.sparta.springauth.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {

    Optional<User> findByUsername(String username);
    Optional<User> findByEmail(String email);

}

 

6-4) Controller ๋‚ด ํšŒ์›๊ฐ€์ž… API ์ถ”๊ฐ€

@PostMapping("/user/signup")
public String signup(@ModelAttribute SignupRequestDto signupRequestDto) {
    userService.signup(signupRequestDto);

    return "redirect:/api/user/login-page"; // ๋กœ๊ทธ์ธ ํ™”๋ฉด ๋ฆฌํ„ด
}

์ผ๋ฐ˜ ์‚ฌ์šฉ์ž ํšŒ์›๊ฐ€์ž… ๊ฒฐ๊ณผ๋ฌผ
๊ด€๋ฆฌ์ž ์‚ฌ์šฉ์ž ํšŒ์›๊ฐ€์ž… ๊ฒฐ๊ณผ๋ฌผ


๋กœ๊ทธ์ธ ๊ตฌํ˜„: JWT

๋กœ๊ทธ์ธ API ๊ตฌํ˜„

Name Method URL ์„ค๋ช…
๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ GET /api/user/login-page ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ
๋กœ๊ทธ์ธ POST /api/user/login ๋กœ๊ทธ์ธ

 

7. ๋กœ๊ทธ์ธ API ๋งŒ๋“ค๊ธฐ

7-1) ๋กœ๊ทธ์ธ RequestDto ์ƒ์„ฑ

package com.sparta.springauth.dto;

import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class LoginRequestDto {
    private String username;
    private String password;
}

 

7-2) ๋กœ๊ทธ์ธ Controller ์ƒ์„ฑ

@PostMapping("/user/login")
public String login(@ModelAttribute LoginRequestDto requestDto, HttpServletResponse res) {
    try {
        userService.login(requestDto, res);
    } catch (Exception e) {
        return "redirect:/api/user/login-page?error";
    }

    return "redirect:/"; // ๋ฉ”์ธ ํ™”๋ฉด์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
}

 

7-3) ๋กœ๊ทธ์ธ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง Service์— ์ถ”๊ธฐ

public void login(LoginRequestDto requestDto, HttpServletResponse res) {
    String username = requestDto.getUsername();
    String password = requestDto.getPassword();

    // ์‚ฌ์šฉ์ž ํ™•์ธ
    User user = userRepository.findByUsername(username).orElseThrow(
            () -> new IllegalArgumentException("๋“ฑ๋ก๋œ ์‚ฌ์šฉ์ž๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
    );

    // ๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ
    if(!passwordEncoder.matches(password, user.getPassword())) {
        throw new IllegalArgumentException("๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
    }

    // JWT ์ƒ์„ฑ ๋ฐ ์ฟ ํ‚ค์— ์ €์žฅ ํ›„ Response ๊ฐ์ฒด์— ์ถ”๊ฐ€
    String token = jwtUtil.createToken(user.getUsername(), user.getRole());
    jwtUtil.addJwtToCookie(token, res);
}

์ผ๋ฐ˜ ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ ๊ฒฐ๊ณผ ํ™”๋ฉด


ํ•„ํ„ฐ(Filter)

ํ•„ํ„ฐ ๋™์ž‘ ๊ณผ์ •

ํ•„ํ„ฐ(Filter)๋Š” Web Application์—์„œ ๊ด€๋ฆฌ๋˜๋Š” ์˜์—ญ์œผ๋กœ, ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ์˜ค๋Š” ์š”์ฒญ๊ณผ ์‘๋‹ต์— ๋Œ€ํ•ด์„œ ์ตœ์ดˆ, ์ตœ์ด ๋‹จ๊ณ„์— ์œ„์น˜ํ•ด ์ด๋ฅผ ํ†ตํ•ด์„œ ์š”์ฒญ๊ณผ ์‘๋‹ต์˜ ์ •๋ณด๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ

  • ์ฃผ๋กœ ๋ฒ”์šฉ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ์ž‘์—…์ธ Logging ํ˜น์€ ๋ณด์•ˆ ์ฒ˜๋ฆฌ์— ํ™œ์šฉํ•จ
  • ์ธ์ฆ, ์ธ๊ฐ€์™€ ๊ด€๋ จ๋œ ๋กœ์ง ์ฒ˜๋ฆฌ๋„ ๊ฐ€๋Šฅํ•จ. ์ฆ‰, ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ธ์ฆ/์ธ๊ฐ€์™€ ๊ด€๋ จ๋œ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•จ์œผ๋กœ์จ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์  ์กด์žฌ
  • Filter๋Š” ํ•˜๋‚˜๋งŒ ์กด์žฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์—ฌ๋Ÿฌ ๊ฐœ๋กœ Chain ํ˜•์‹์œผ๋กœ ๋ฌถ์—ฌ์„œ ์ฒ˜๋ฆฌ๋˜๊ณ  ์žˆ์Œ

Chain ํ˜•์‹์˜ Filter

URL์„ Loggingํ•˜๋Š” Filter

package com.sparta.springauth.filter;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Slf4j(topic = "LoggingFilter") // ๋กœ๊น…๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ๋กœ๊น… ์˜†์— topic ์ด๋ฆ„์œผ๋กœ ์ฐํž˜
@Component
@Order(1) // Chain ํ˜•์‹์œผ๋กœ ๋˜์–ด์žˆ๋Š” Filter์— ์ˆœ์„œ ์ง€์ • ๊ฐ€๋Šฅ
public class LoggingFilter implements Filter {
    // Filter ์ธํ„ฐํŽ˜์ด์Šค ๋‚ด์— doFilter ๋ฉ”์„œ๋“œ ์กด์žฌํ•จ
    // FilterChain์€ Filter๋ฅผ ์ด๋™ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ๋ฐ›์•„์˜ด
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // ์ „์ฒ˜๋ฆฌ
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String url = httpServletRequest.getRequestURI(); // URL ์ •๋ณด ๊ฐ€์ ธ์˜ด
        log.info(url); // log ์ฐ๊ธฐ

        chain.doFilter(request, response); // ๋‹ค์Œ Filter ๋กœ ์ด๋™

        // ํ›„์ฒ˜๋ฆฌ
        log.info("๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์™„๋ฃŒ");
    }
}

์ธ์ฆ ๋ฐ ์ธ๊ฐ€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” Filter

  • ์ด์ „์— Filter๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  Controller๋กœ ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๋•Œ๋Š”, @CookieValue ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์„œ Request ๊ฐ์ฒด์˜ Cookie์— ๋“ค์–ด์žˆ๋Š” Value๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์—ˆ์Œ
  • Filter๋Š” DispatcherServlet๋ณด๋‹ค ์•ž๋‹จ์ด๊ธฐ ๋•Œ๋ฌธ์— @CookieValue ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์—, Cookie๋ฅผ ๋ฝ‘์•„๋‚ด๋Š” ๋ฉ”์„œ๋“œ ์ง์ ‘ ๊ตฌํ˜„ ํ•„์š”ํ•จ

JwtUtil์— Cookie ๊ฐ€์ ธ์˜ค๋Š” ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€

// HttpServletRequest ์—์„œ Cookie Value : JWT ๊ฐ€์ ธ์˜ค๊ธฐ
public String getTokenFromRequest(HttpServletRequest req) {
    Cookie[] cookies = req.getCookies();
    if(cookies != null) {
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals(AUTHORIZATION_HEADER)) {
                try {
                    return URLDecoder.decode(cookie.getValue(), "UTF-8"); // Encode ๋˜์–ด ๋„˜์–ด๊ฐ„ Value ๋‹ค์‹œ Decode
                } catch (UnsupportedEncodingException e) {
                    return null;
                }
            }
        }
    }
    return null;
}

 

์ธ์ฆ FIlter ์ƒ์„ฑ

package com.sparta.springauth.filter;

import com.sparta.springauth.entity.User;
import com.sparta.springauth.jwt.JwtUtil;
import com.sparta.springauth.repository.UserRepository;
import io.jsonwebtoken.Claims;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.IOException;

@Slf4j(topic = "AuthFilter")
@Component
@Order(2)
public class AuthFilter implements Filter {

    private final UserRepository userRepository;
    private final JwtUtil jwtUtil;

    public AuthFilter(UserRepository userRepository, JwtUtil jwtUtil) {
        this.userRepository = userRepository;
        this.jwtUtil = jwtUtil;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String url = httpServletRequest.getRequestURI();

        if (StringUtils.hasText(url) &&
                (url.startsWith("/api/user") || url.startsWith("/css") || url.startsWith("/js"))
        ) {
            // ํšŒ์›๊ฐ€์ž…, ๋กœ๊ทธ์ธ ๊ด€๋ จ API ๋Š” ์ธ์ฆ ํ•„์š”์—†์ด ์š”์ฒญ ์ง„ํ–‰
            chain.doFilter(request, response); // ๋‹ค์Œ Filter ๋กœ ์ด๋™
        } else {
            // ๋‚˜๋จธ์ง€ API ์š”์ฒญ์€ ์ธ์ฆ ์ฒ˜๋ฆฌ ์ง„ํ–‰
            // ํ† ํฐ ํ™•์ธ

            // ์ด์ „์— Filter๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  Controller๋กœ ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๋•Œ๋Š”,
            // @CookieValue ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์„œ Request ๊ฐ์ฒด์˜ Cookie์— ๋“ค์–ด์žˆ๋Š” Value๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์—ˆ์Œ
            // Filter๋Š” DispatcherServlet๋ณด๋‹ค ์•ž๋‹จ์ด๊ธฐ ๋•Œ๋ฌธ์— @CookieValue ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์—,
            // Cookie๋ฅผ ๋ฝ‘์•„๋‚ด๋Š” ๋ฉ”์„œ๋“œ ์ง์ ‘ ๊ตฌํ˜„ ํ•„์š”ํ•จ
            String tokenValue = jwtUtil.getTokenFromRequest(httpServletRequest);

            if (StringUtils.hasText(tokenValue)) { // ํ† ํฐ์ด ์กด์žฌํ•˜๋ฉด ๊ฒ€์ฆ ์‹œ์ž‘
                // JWT ํ† ํฐ substring
                String token = jwtUtil.substringToken(tokenValue);

                // ํ† ํฐ ๊ฒ€์ฆ
                if (!jwtUtil.validateToken(token)) {
                    throw new IllegalArgumentException("Token Error");
                }

                // ํ† ํฐ์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
                Claims info = jwtUtil.getUserInfoFromToken(token);

                User user = userRepository.findByUsername(info.getSubject()).orElseThrow(() ->
                        new NullPointerException("Not Found User")
                );

                request.setAttribute("user", user);
                chain.doFilter(request, response); // ๋‹ค์Œ Filter ๋กœ ์ด๋™
            } else {
                throw new IllegalArgumentException("Not Found Token");
            }
        }
    }

}

๋กœ๊ทธ ์ถœ๋ ฅ ์˜ˆ์‹œ