20220707 WIL

Don Leeยท2022๋…„ 7์›” 7์ผ
0

EpiTIL

๋ชฉ๋ก ๋ณด๊ธฐ
23/24
post-thumbnail
post-custom-banner

Spring Boot and Spring Security with JWT including Access and Refresh Tokens ๐Ÿ”‘

Spring Boot and Spring Security with JWT including Access and Refresh Tokens ๐Ÿ”‘
ํ•ด๋‹น ๊ธ€์˜ ๋ชฉ์ ์€ ์œ„ ๋งํฌ์˜ ๊ฐ•์˜์—์„œ ์ œ๊ฐ€ ์ค‘์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ฑฐ๋‚˜ ์งš๊ณ  ๋„˜์–ด๊ฐˆ ํ•„์š”๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•œ ๋ถ€๋ถ„์„ ์ •๋ฆฌํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. Access Token๊ณผ Refesh Token์„ ์„ค๋ช…ํ•˜๋Š” ์ข‹์€ ๊ฐ•์˜์ด๋‹ˆ JWT๋กœ ํ† ํฐ์„ ๊ตฌํ˜„ํ•˜์‹œ๋Š” ๋ถ„๋“ค์€ ๊ผญ ์ฐธ๊ณ ํ•˜์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

9:40

Spring ์ž์ฒด์— User ํด๋ž˜์Šค๊ฐ€ ์žˆ๊ธฐ์— AppUser๋กœ ํ•œ๋‹ค๋ฉด importํ•  ๋•Œ ํ—ท๊ฐˆ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.

12:40

[JPA] ์ฆ‰์‹œ ๋กœ๋”ฉ, ์ง€์—ฐ ๋กœ๋”ฉ | FetchType.EAGER, FetchType.LAZY

public class User {
...
    @ManyToMany(fetch = FetchType.EAGER)
    private Collection<Role> roles = new ArrayList<>();
}

21:00

public interface UserService {
...
    List<User> getUsers();

getUsers() ๋ฆฌ์ŠคํŠธ๋ฅผ ์ „๋‹ฌํ•˜๋ผ๊ณ  ํ–ˆ์„ ๋•Œ, ์œ ์ €๊ฐ€ ๋งŒ์•ฝ์— 5์–ต๋ช…์ด๋ผ๋ฉด? ๊ทธ๋Ÿฌ๋ฉด ์ „๋ถ€ ์ „๋‹ฌํ•˜๊ธฐ์—” ๋ฐฑ์—”๋“œ์— ๋ถ€๋‹ด์ด ๋  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿด๊ฒฝ์šฐ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ๋ฅผํ•ด์„œ 10~100๋ช… ๋‹จ์œ„๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด ํšจ๊ณผ์ ์ผ ๊ฒƒ์ด๋‹ค. ๋งŒ์•ฝ์— 2ํŽ˜์ด์ง€๋ฅผ ๋ณด๋ฉด ์ƒˆ๋กœ์šด 10~100๋ช…์„ ๋ณด๋‚ด๋ฉด ๋˜๋‹ˆ๊นŒ ๋ง์ด๋‹ค.

์—ฌ๊ธฐ์„  ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ด๊ธฐ์— ๊ทธ๋Ÿฌํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค„ ํ•„์š”๊ฐ€ ์—†๋‹ค. ํ•˜์ง€๋งŒ ๋งŒ์•ฝ impl์„ ๋งŒ๋“ ๋‹ค๋ฉด ๊ทธ๋Ÿฌํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ์ด๋‹ค.

24:50

๐Ÿ“ข `@Transactional` ๊ณต๋ถ€ํ•˜๊ธฐ, `Collection`๋„ ๊ณต๋ถ€ํ•˜๊ธฐ

@Transactional์ด ์žˆ๊ธฐ๋•Œ๋ฌธ์— userRepository๋ฅผ ๋ถˆ๋Ÿฌ์„œ ๋‹ค์‹œ ์ €์žฅํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ๋งํ•œ๋‹ค.

34:00

์Šคํ”„๋ง์ด session์„ ์ด์šฉํ•ด ๋กœ๊ทธ์ธ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ์•„๋ž˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.



37:00

`created(null)`์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ .

Spring Boot๋ฅผ ์ด์šฉํ•œ RESTful Web Services ๊ฐœ๋ฐœ #14 HTTP Status Code ์ œ์–ด

41:30

๐Ÿ“ข `ResponseEntity` ๊ณต๋ถ€ํ•˜๊ธฐ
@PostMapping("/role/addtouser")
    public ResponseEntity<?> addRoleToUser(@RequestBody RoleToUserForm form) {
        userService.addRoleToUser(form.getUsername(), form.getRoleName());
        return ResponseEntity.ok().build();
    }

ResponseEntity<?>๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ .build()๋กœ ๋งˆ๋ฌด๋ฆฌ ํ•˜๋Š” ์ด์œ ๋Š”?

45:50

CommandLineRunner

์Šคํ”„๋ง ๋ถ€ํŠธ ๊ตฌ๋™ ์‹œ์ ์— ํŠน์ • ์ฝ”๋“œ ์‹คํ–‰ ์‹œํ‚ค๊ธฐ (CommandLineRunner & ApplicationRunner)

51:00

Spring์ด User๋ฅผ ์ฐพ๋Š” ์„ธ๊ฐ€์ง€ ๋ฐฉ๋ฒ•

auth.inMemoryAuthentication()
auth.jdbcAuthentication()
auth.userDetailsService()
  • auth.inMemoryAuthentication()spring์ด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋’ค์ ๊ฑฐ๋ ค์„œ ์œ ์ €๋ฅผ ์ฐพ๋Š” ๋ฐฉ๋ฒ•. ๊ฐ€์žฅ ์›์ดˆ์ (?)์ธ๋“ฏ?
  • auth.jdbcAuthentication()spring์ด JDBC๋ฅผ ์‚ฌ์šฉํ•ด ์œ ์ €๋ฅผ ์ฐพ๋Š” ๋ฐฉ๋ฒ•. JPA๊ฐ€ ์žˆ๊ธฐ์— JDBC๋ฅผ ์ด์šฉํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
  • auth.userDetailsService() JPA๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ• โ† ์ด๊ฑธ๋กœ ์ง„ํ–‰.

1:05:00

//๋ช‡ ๋ถ„ ๋™์•ˆ ๋ช‡ ๋ฒˆ ๋กœ๊ทธ์ธ ์ œํ•œ๊ณผ ๊ฐ™์€ ๋กœ๊ทธ์ธ ์‹คํŒจ ์‹œ ๋™์ž‘๋“ค์€ ์•„๋ž˜์˜ method์—์„œ ์‹คํ–‰๋œ๋‹ค.
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        super.unsuccessfulAuthentication(request, response, failed);
    }

unsuccessfulAuthentication()์€ ์ด๋ฒˆ ๊ฐ•์˜์—์„œ ๋‹ค๋ฃจ์ง€ ์•Š๊ณ  security ๊ฐ•์˜์—์„œ ๋‹ค๋ฃฌ๋‹ค๊ณ  ํ•œ๋‹ค.

Spring Security | FULL COURSE

์•„๋งˆ ์—ฌ๊ธฐ์—์„œ ๋‹ค๋ฃจ๋Š” ๋“ฏ ํ•˜๋‹ค.

๐Ÿ“ข ๋‚˜์ค‘์— ์œ„์˜ ๊ฐ•์˜๋„ ๋“ค์–ด๋ณผ ๊ฒƒ.

1:23:20

@Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

request.getParameter()์„ ์‚ฌ์šฉํ•˜๊ธฐ์— postman์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

โ“ request.getParameter์™€ x-www-form-urlencoded๋ฅผ ์™œ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค.

โœ… ์•„๋ž˜์™€ ๊ฐ™์ด ์ง๋ ฌํ™”๋ฅผ HashMap์„ ํ†ตํ•ด ํ–ˆ๊ธฐ์— ์ด์˜๊ฒŒ json ํ˜•์‹์œผ๋กœ accessToken๊ณผ refreshToken์ด ์ถœ๋ ฅ๋œ๋‹ค.
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
		HashMap<String, String> tokens = new HashMap<>();
		tokens.put("accessToken", accessToken);
		tokens.put("refreshToken", refreshToken);
		response.setContentType(APPLICATION_JSON_VALUE);
		new ObjectMapper().writeValue(response.getOutputStream(), tokens);

1:27:20

@Override
    protected void configure(HttpSecurity http) throws Exception {
...
        http.authorizeRequests().anyRequest().permitAll();

anyRequest().permitAll(); ์ด ๋˜์–ด์žˆ๊ธฐ์— ๋ณด์•ˆ์ด ๋˜์–ด์žˆ๋‹ค๊ณ  ๋ณด๊ธฐ ์–ด๋ ต๋‹ค. ๊ทธ๋ ‡๊ธฐ์— ํŠน์ • ์œ ์ €๋‚˜ ์ƒํ™ฉ์—๊ฒŒ๋งŒ ๊ถŒํ•œ์„ ์ฃผ๋„๋ก ํ•˜์ž. ์ธ๊ฐ€๋œ ์ด๋งŒ ์ ‘๊ทผ์„ ํ•ด์•ผํ•˜๊ธฐ์— authenticated()๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜์ž.

์—ฌ๊ธฐ์„œ GET์€ HttpMethod.GET์ด๋‹ค.

1:30:30

public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
...
    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER 
				= new AntPathRequestMatcher("/login", "POST");
...
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
...

CustomAuthenticationFilterโ†’UsernamePasswordAuthenticationFilter์„ ๊ฐ€๋ณด๋ฉด default๋กœ AntPathRequestMatcher("/login", "POST")์ด ๋˜์–ด์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ณ„๋„๋กœ ์•„๋ž˜์™€ ๊ฐ™์ด antMatchers("/login")๋ฅผ ์„ค์ •ํ•  ํ•„์š”๋Š” ์—†๋‹ค.

@Override
    protected void configure(HttpSecurity http) throws Exception {
...
//      http.authorizeRequests().antMatchers("/login").permitAll();
        http.authorizeRequests().antMatchers("").permitAll();

๋‹ค๋งŒ ํ†ต์ผ์„ฑ์„ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด overwriteํ•œ๋‹ค.

@Override
    protected void configure(HttpSecurity http) throws Exception {
        CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter(authenticationManagerBean());
        customAuthenticationFilter.setFilterProcessesUrl("/api/login");
...

        http.authorizeRequests().antMatchers("/api/login/**").permitAll();
...

        http.addFilter(customAuthenticationFilter);
    }

postmanํ•ด๋ณด๋ฉด ์ด์˜๊ฒŒ ๋‚˜์˜ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

1:37:20

ํด๋ผ์ด์–ธํŠธ๋Š” token์ด๋ž‘ ๊ฐ™์ด ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ โ€˜bearerโ€™์„ ๊ฐ™์ด ๋ถ™์—ฌ์„œ ์š”์ฒญ์„ ๋ณด๋‚ผ ๊ฒƒ์ด๋‹ค. ์œ ์ €๊ฐ€ ํ•œ ๋ฒˆ validated๋˜๊ณ  ํ† ํฐ์„ ๋ฐœ๊ธ‰ ๋ฐ›์•˜๊ธฐ์— ๊ทธ ์ดํ›„์˜ ์š”์ฒญ์— ์žˆ์–ด์„œ permission์„ ๋ฐ›์„ ํ•„์š”๊ฐ€ ์—†๋‹ค. ๊ทธ๋ ‡๊ธฐ์— ํ•ด๋‹น ์œ ์ €๊ฐ€ ์ƒ์„ฑ์ž, ์ฆ‰ โ€˜bearerโ€™์ด๋ผ๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์ฃผ๊ธฐ์œ„ํ•ด ํ•ด๋‹น ๋ฌธ๊ตฌ๋ฅผ ์‚ฝ์ž…ํ•œ๋‹ค.

1:53:25

accessToken์—†์ด GET์š”์ฒญ์„ ํ•˜๋ฉด 403์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

profile
์พŒ๋ฝ์ฝ”๋”ฉ
post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€