๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Spring Security

[Spring Security] ์Šคํ”„๋ง์‹œํ๋ฆฌํ‹ฐ ์‹œ์ž‘ํ•˜๊ธฐ / ๊ธฐ๋ณธ์„ธํŒ…

๐Ÿ’ก ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์‹œ์ž‘ํ•˜๊ธฐ(๊ธฐ๋ณธ์„ธํŒ…)

1.  dependency ์ถ”๊ฐ€

<Maven ์ด์šฉ> 

<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-web</artifactId>
	<version>4.2.2.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-config</artifactId>
	<version>4.2.2.RELEASE</version>
</dependency>
</dependencies>

 

<Gradle ์ด์šฉ>

dependencies {
	compile 'org.springframework.security:spring-security-web:4.2.2.RELEASE'
	compile 'org.springframework.security:spring-security-config:4.2.2.RELEASE'
}

๋ฒ„์ „์€ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ์ ํŠธ์— ๋งž๊ฒŒ ๋ฐ”๊ฟ”์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

2. Java Configuration

WebSecurityConfigurerAdapter๋ฅผ ์ƒ์†๋ฐ›์€ config ํด๋ž˜์Šค์— @EnableWebSecurity ์–ด๋…ธํ…Œ์ด์…˜์„ ๋‹ฌ๋ฉดSpringSecurityFilterChain์ด ์ž๋™์œผ๋กœ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}

์ž‘์„ฑํ•œ ๋’ค, configure ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•˜์—ฌ ์ ‘๊ทผ ๊ถŒํ•œ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

 ์˜ˆ์‹œ ์ฝ”๋“œ 1 ) ๊ฐ ์ƒํ™ฉ๋ณ„ ๋Œ€์‘ ๋ฐฉ๋ฒ• ์„ค์ • &

@EnableWebSecurity  //์Šคํ”„๋ง์‹œํ๋ฆฌํ‹ฐ ์‚ฌ์šฉ์„ ์œ„ํ•œ ์–ด๋…ธํ…Œ์ด์…˜์„ ์–ธ

public class SpringSecurityConfig extends WebSecurityConfigurerAdapter{
//WebSecurityConfigurerAdapter ์ƒ์†
    
    private AuthenticationProvider authenticationProvider;
    
    public SpringSecurityConfig(/*UserDetailsService userDetailsService, 
                                PasswordEncoder passwordEncoder,*/
                                AuthenticationProvider authenticationProvider) {

        this.authenticationProvider = authenticationProvider;
    }
    
    /*
     * ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๋‹ด๊ธด ๊ฐ์ฒด.
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        /*
         * AuthenticationProvider ๊ตฌํ˜„์ฒด
         */
        auth.authenticationProvider(authenticationProvider);
//        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }
    
    /*
     * ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ๋ฃฐ์„ ๋ฌด์‹œํ•˜๊ฒŒ ํ•˜๋Š” Url ๊ทœ์น™(์—ฌ๊ธฐ ๋“ฑ๋กํ•˜๋ฉด ๊ทœ์น™ ์ ์šฉํ•˜์ง€ ์•Š์Œ)
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
            .antMatchers("/resources/**")
            .antMatchers("/css/**")
            .antMatchers("/vendor/**")
            .antMatchers("/js/**")
            .antMatchers("/favicon*/**")
            .antMatchers("/img/**")
        ;
    }
    
    /*
     * ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ๊ทœ์น™
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()//๋ณดํ˜ธ๋œ ๋ฆฌ์†Œ์Šค URI์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์„ ์„ค์ •
            .antMatchers("/login*/**").permitAll() //์ „์ฒด ์ ‘๊ทผ ํ—ˆ์šฉ
            .antMatchers("/logout/**").permitAll()
            .antMatchers("/myPage").hasRole("ADMIN")//admin์ด๋ผ๋Š” ๋กค์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผ ํ—ˆ์šฉ
            .antMatchers("/chatbot/**").permitAll()
            .anyRequest().authenticated()
        .and().logout()
              .logoutUrl("/logout")
              .logoutSuccessHandler(logoutSuccessHandler())
        .and().csrf()//csrf ๋ณด์•ˆ ์„ค์ •์„ ๋น„ํ™œ์„ฑํ™”
              .disable()//ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ”„๋ก ํŠธ๋‹จ์—์„œ csrfํ† ํฐ๊ฐ’ ๋ณด๋‚ด์ค˜์•ผํ•จ
        .addFilter(jwtAuthenticationFilter())//Form Login์— ์‚ฌ์šฉ๋˜๋Š” custom AuthenticationFilter ๊ตฌํ˜„์ฒด๋ฅผ ๋“ฑ๋ก
        .addFilter(jwtAuthorizationFilter())//Header ์ธ์ฆ์— ์‚ฌ์šฉ๋˜๋Š” BasicAuthenticationFilter ๊ตฌํ˜„์ฒด๋ฅผ ๋“ฑ๋ก
        .exceptionHandling()
              .accessDeniedHandler(accessDeniedHandler())
              .authenticationEntryPoint(authenticationEntryPoint())
        ;
    }
    
    /*
     * SuccessHandler bean register
     */
    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
        CustomAuthenticationSuccessHandler successHandler = new CustomAuthenticationSuccessHandler();
        successHandler.setDefaultTargetUrl("/index");
        return successHandler;
    }
    
    /*
     * FailureHandler bean register
     */
    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler() {
        CustomAuthenticationFailureHandler failureHandler = new CustomAuthenticationFailureHandler();
        failureHandler.setDefaultFailureUrl("/loginPage?error=error");
        return failureHandler;
    }
    
    /*
     * LogoutSuccessHandler bean register
     */
    @Bean
    public LogoutSuccessHandler logoutSuccessHandler() {
        CustomLogoutSuccessHandler logoutSuccessHandler = new CustomLogoutSuccessHandler();
        logoutSuccessHandler.setDefaultTargetUrl("/loginPage?logout=logout");
        return logoutSuccessHandler;
    }
    
    /*
     * AccessDeniedHandler bean register
     */
    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        CustomAccessDeniedHandler accessDeniedHandler = new CustomAccessDeniedHandler();
        accessDeniedHandler.setErrorPage("/error/403");
        return accessDeniedHandler;
    }
    
    /*
     * AuthenticationEntryPoint bean register
     */
    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return new CustomAuthenticationEntryPoint("/loginPage?error=e");
    }
    
    /*
     * Form Login์‹œ ๊ฑธ๋ฆฌ๋Š” Filter bean register
     */
    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager());
        jwtAuthenticationFilter.setFilterProcessesUrl("/login");
        jwtAuthenticationFilter.setUsernameParameter("username");
        jwtAuthenticationFilter.setPasswordParameter("password");
        
        jwtAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler());
        jwtAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
        
        jwtAuthenticationFilter.afterPropertiesSet();
        
        return jwtAuthenticationFilter;
    }
    
    /*
     * Filter bean register
     */
    @Bean
    public JwtAuthorizationFilter jwtAuthorizationFilter() throws Exception {
        JwtAuthorizationFilter jwtAuthorizationFilter = new JwtAuthorizationFilter(authenticationManager());
        return jwtAuthorizationFilter;
    }
    @Bean
	public PasswordEncoder passwordEncoder() {//๊ฐ„๋‹จํ•˜๊ฒŒ ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™”
		return new BCryptPasswordEncoder(); 
	}
}

์—ฌ๊ธฐ ์‚ฌ์šฉ๋œ BCryptPasswordEncoder ์•”ํ˜ธํ™”์— ๋Œ€ํ•ด์„œ๋Š” ์•„๋ž˜๋งํฌ์—์„œ ๋” ํ•™์Šตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

https://jaimemin.tistory.com/2082

 

[SpringBoot] BcryptPasswordEncoder

๊ฐœ์š” ์‹ค์ œ ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•ด ๋ณธ ๊ฒฝํ—˜์ด ์žˆ๋Š” ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ DB์— ํ‰๋ฌธ์œผ๋กœ ์ €์žฅํ•˜๋ฉด ์ ˆ๋Œ€ ์•ˆ ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋ณดํ†ต ์‚ฌ๋žŒ๋“ค์€ ์—ฌ๋Ÿฌ ์‚ฌ์ดํŠธ์— ๋™์ผํ•œ ์•„์ด๋””

jaimemin.tistory.com

 

 

 

์ฝ”๋“œ ๋‚ด์šฉ ์„ค๋ช… 

๐Ÿ“Œ ๋‹ค์Œ์€ antMatchers() ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š” ํ•ญ๋ชฉ๋“ค์ž…๋‹ˆ๋‹ค.

  • hasRole() or hasAnyRole()
    ํŠน์ • ๊ถŒํ•œ์„ ๊ฐ€์ง€๋Š” ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • hasAuthority() or hasAnyAuthority()
    ํŠน์ • ๊ถŒํ•œ์„ ๊ฐ€์ง€๋Š” ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • hasIpAddress()
    ํŠน์ • ์•„์ดํ”ผ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ง€๋Š” ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • permitAll() or denyAll()
    ์ ‘๊ทผ์„ ์ „๋ถ€ ํ—ˆ์šฉํ•˜๊ฑฐ๋‚˜ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.
  • rememberMe()
    ๋ฆฌ๋ฉค๋ฒ„ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • anonymous()
    ์ธ์ฆ๋˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • authenticated()
    ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

andMatcher ์™€ mvcMatcher ์˜ ์ฐจ์ด

antMatcher(String antPattern) - Allows configuring the HttpSecurity to only be invoked when matching the provided ant pattern.

mvcMatcher(String mvcPattern) - Allows configuring the HttpSecurity to only be invoked when matching the provided Spring MVC pattern.

AntMatcher() is an implementation for Ant-style path patterns. Part of this mapping code has been kindly borrowed from Apache Ant.

MvcMatcher() uses Spring MVC's HandlerMappingIntrospector to match the path and extract variables.

So they both implement RequestMatcher interface, but use different expression languages under the hood.

 

Generally mvcMatcher is more secure than an antMatcher. As an example:

  • antMatchers("/secured") matches only the exact /secured URL 
  • => ์ฆ‰ ๊ธฐ์žฌํ•ด์ค€ url ์— ๋Œ€ํ•ด์„œ๋งŒ security ์ ์šฉ
  • mvcMatchers("/secured") matches /secured as well as /secured/, /secured.html, /secured.xyz
  • => ํŒŒ์ผ๋ช…์˜ prefix ์— ํ•ด๋‹น๋˜๋Š” ๋ชจ๋“  ํ•˜์œ„ ํŒŒ์ผ์— ๋Œ€ํ•ด ์ „๋ถ€ security ์ ์šฉ๋˜๋Š” ๋“ฏ

and therefore is more general and can also handle some possible configuration mistakes.

mvcMatcher uses the same rules that Spring MVC uses for matching (when using @RequestMapping annotation).

If the current request will not be processed by Spring MVC, a reasonable default using the pattern as a ant pattern will be used. Source

It may be added that mvcMatchers API (since 4.1.1) is newer than the antMatchers API (since 3.1).

antMatcher("/users/**") matches any path starting with /users
antMatchers("/users") matches only the exact /users URL
mvcMatchers("/users") matches /users, /users/, /users.html

 

 

 

 

Role์€ ์—ญํ• ์ด๊ณ  Authority๋Š” ๊ถŒํ•œ์ด์ง€๋งŒ ์‚ฌ์‹ค์€ ํ‘œํ˜„์˜ ์ฐจ์ด์ž…๋‹ˆ๋‹ค. 
Role์€ “ADMIN”์œผ๋กœ ํ‘œํ˜„ํ•˜๊ณ  Authority๋Š” “ROLE_ADMIN”์œผ๋กœ ํ‘œ๊ธฐํ•ฉ๋‹ˆ๋‹ค.


Role ๊ณผ Permission ์˜ ์ฐจ์ด
Permission ์€ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ๋“ค์„ ๋‹จ์œ„๋กœ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ฒƒ

Role ์€ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ๋“ค์„ ๋ฌถ์–ด์„œ ๊ทธ๊ฒƒ๋“ค์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์—ญํ• ์„ ์นญํ•˜๋Š” ๊ฒƒ


Roles and Permissions using ENUMS

 

Role Based Authorities

package com.example.demo.security;

import com.google.common.collect.Sets;

import java.util.Set;

import static com.example.demo.security.ApplicationUserPermission.*;

public enum ApplicationUserRole {
    STUDENT(Sets.newHashSet()),
    ADMIN(Sets.newHashSet(COURSE_READ, COURSE_WRITE, STUDENT_READ, STUDENT_WRITE)),
    ADMINTRAINEE(Sets.newHashSet(COURSE_READ, STUDENT_READ));

    private final Set<ApplicationUserPermission> permissions;

    ApplicationUserRole(Set<ApplicationUserPermission> permissions) {
        this.permissions = permissions;
    }

    public Set<ApplicationUserPermission> getPermissions() {
        return permissions;
    }
}

 

Permission Based Authentication

package com.example.demo.security;

public enum ApplicationUserPermission {
    STUDENT_READ("student:read"),
    STUDENT_WRITE("student:write"),
    COURSE_READ("course:read"),
    COURSE_WRITE("course:write");

    private final String permission;

    ApplicationUserPermission(String permission) {
        this.permission = permission;
    }

    public String getPermission() {
        return permission;
    }
}

 

 

3.UserDetails ๊ตฌํ˜„

user detail ์„ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” UserDetails ๋ฅผ Implements ํ•ด์ค๋‹ˆ๋‹ค.

1 @Slf4j
2 @Service
3 public class CustomUserDetailService implements AuthenticationUserDetailsService<Authentication> {
4
5    @Override
6    public UserDetails loadUserDetails(Authentication token) {
7        User user = (User) token.getPrincipal();
8
9        if (user == null) {
10            throw new PreAuthenticatedCredentialsNotFoundException("USER IS NULL");
11        }
12
13                // DB์— ์ ‘๊ทผํ•ด์„œ ์ง์ ‘ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”๊ฒŒ ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค.
14    
15        return new CustomUserDetails().setUser(user).setGrantedAuthorities(user.getAuthorities());
16    }
17
18 }

์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” AuthenticationUserDetailsService ๋ฅผ implementํ•ฉ๋‹ˆ๋‹ค.

4. AuthenticationSuccessHandler ๊ตฌํ˜„

Form Login(AuthenticationFilter)์—์„œ ์ธ์ฆ์ด ์„ฑ๊ณตํ–ˆ์„ ๋•Œ ์ˆ˜ํ–‰๋  ํ•ธ๋“ค๋Ÿฌ

SimpleUrlAuthenticationSuccessHandler๋ฅผ ์ƒ์†๋ฐ›์•„ ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

์ถ”์ƒํด๋ž˜์Šค๊ฐ€ ์•„๋‹ˆ๋ผ๋„ ์ธํ„ฐํŽ˜์ด์Šคํ˜•ํƒœ๋กœ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์„ฑ๊ณต์‹œ ์ธ์ฆํ† ํฐ์„ ์ฟ ํ‚ค์— ๋„ฃ์–ด์ฃผ๊ฑฐ๋‚˜, index ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

@Slf4j
public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler implements ExceptionProcessor{
    
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws ServletException, IOException {
        log.debug("CustomAuthenticationSuccessHandler.onAuthenticationSuccess ::::");
        /*
         * ์ฟ ํ‚ค์— ์ธ์ฆ ํ† ํฐ์„ ๋„ฃ์–ด์ค€๋‹ค.
         */
        super.onAuthenticationSuccess(request, response, authentication);
    }
 
    @Override
    public void makeExceptionResponse(HttpServletRequest request, HttpServletResponse response,
            Exception exception) {
    }
    
}

5. AuthenticationFailureHandler ๊ตฌํ˜„

Form Login ์‹คํŒจ์‹œ ์ˆ˜ํ–‰๋˜๋Š” ํ•ธ๋“ค๋Ÿฌ

SimpleUrlAuthenticationFailureHandler๋ฅผ ์ƒ์†๋ฐ›์œผ๋ฉฐ, ์‹คํŒจ์‹œ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

@Slf4j
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler implements ExceptionProcessor{
    
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException {
        log.debug("CustomAuthenticationFailureHandler.onAuthenticationFailure ::::");
        super.onAuthenticationFailure(request, response, exception);
    }
 
    @Override
    public void makeExceptionResponse(HttpServletRequest request, HttpServletResponse response,
            Exception exception) {
    }
    
}

 

6. LogoutSuccessHandler ๊ตฌํ˜„

๋กœ๊ทธ์•„์›ƒ์— ์„ฑ๊ณตํ–ˆ์„ ์‹œ ์ˆ˜ํ–‰๋˜๋Š” ํ•ธ๋“ค๋Ÿฌ

SimpleUrlLogoutSuccessHandler๋ฅผ ์ƒ์†ํ•˜๋ฉฐ,๋กœ๊ทธ์•„์›ƒ ์„ฑ๊ณต์‹œ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

@Slf4j
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler{
    
    @Override    
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {
        log.debug("CustomLogoutSuccessHandler.onLogoutSuccess ::::");
        super.onLogoutSuccess(request, response, authentication);
    }
    
}

 

7. JwtAuthenticationFilter ๊ตฌํ˜„

Form Login์‹œ ๊ฑธ๋ฆฌ๋Š” Filter

UsernamePasswordAuthenticationFilter๋ฅผ ์ƒ์†ํ•˜๋ฉฐ, ์‚ฌ์šฉ์ž๊ฐ€ Form์œผ๋กœ ์ž…๋ ฅํ•œ ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ์ธํ„ฐ์…‰ํŠธํ•ด์„œ AuthenticationManager์—๊ฒŒ Authentication ๊ฐ์ฒด๋ฅผ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค.

@Slf4j
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
    
    private boolean postOnly = true;
    
    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super.setAuthenticationManager(authenticationManager);
    }
    
    /*
     * ํ•ด๋‹น ํ•„ํ„ฐ์—์„œ ์ธ์ฆ ํ”„๋กœ์„ธ์Šค ์ด์ „์— ์š”์ฒญ์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€์„œ
     * Authentication ๊ฐ์ฒด๋ฅผ ์ธ์ฆ ํ”„๋กœ์„ธ์Šค ๊ฐ์ฒด์—๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ์—ญํ• 
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        log.debug("JwtAuthentication.attemptAuthentication ::::");
        
        /*
         * POST๋กœ ๋„˜์–ด์™”๋Š”์ง€ ์ฒดํฌ
         */
        if(postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
        
        String username = obtainUsername(request);
        String password = obtainPassword(request);
        
        if(StringUtils.isEmpty(username)) {
            username = "";
        }
        if(StringUtils.isEmpty(password)) {
            password = "";
        }
        
        username = username.trim();
        
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        
        setDetails(request, authRequest);
        
        return this.getAuthenticationManager().authenticate(authRequest);
    }
    
    
}

 

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋Š” ์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•˜๊ณ , ๋กœ๊ทธ์ธ ํ›„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ฐ ๊ธฐ๋Šฅ๋“ค์— ๋Œ€ํ•œ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ๊ฐ ํ•ธ๋“ค๋Ÿฌ, ํ•„ํ„ฐ๋“ค์„ ๊ฑฐ์ณ ๋™์ž‘ํ•œ๋‹ค๋Š” ๊ฒƒ์ด ํฌ์ธํŠธ์ž…๋‹ˆ๋‹ค. ์Šคํ”„๋ง์‹œํ๋ฆฌํ‹ฐ์—๋Š” ๋งŽ์€ ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›๊ณ  ๋ณต์žกํ•˜๊ฒŒ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์–ด, ๋„์‹ํ™”๋œ ๊ทธ๋ฆผ์„ ๋ณด๋ฉด ์ „์ฒด์ ์ธ ๊ตฌ์กฐ๋‚˜ ํ๋ฆ„์„ ์ดํ•ดํ•˜๋Š”๋ฐ ๋„์›€์ด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. 

 

 

 

 

์ถ”๊ฐ€๋กœ ์•Œ๋ฉด ์ข‹์„ ๋‚ด์šฉ๋“ค

 

HTTP ์ธ์ฆ ๋ฉ”์ปค๋‹ˆ์ฆ˜ : ์ธ์ฆ ํ—ค๋”์— base-64  ์ธ์ฝ”๋”ฉ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ๊ฐ€์„œ ์ธ์ฆ์„ ๋ฐ›๋Š”๋‹ค

๋‹ค์–‘ํ•œ Authentication Header Types

์ธ์ฆ ํƒ€์ž…

Basic

์‚ฌ์šฉ์ž ์•„์ด๋””์™€ ์•”ํ˜ธ๋ฅผ Base64๋กœ ์ธ์ฝ”๋”ฉํ•œ ๊ฐ’์„ ํ† ํฐ์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค. (RFC 7617)

Bearer

JWT ํ˜น์€ OAuth์— ๋Œ€ํ•œ ํ† ํฐ์„ ์‚ฌ์šฉํ•œ๋‹ค. (RFC 6750)

Digest

์„œ๋ฒ„์—์„œ ๋‚œ์ˆ˜ ๋ฐ์ดํ„ฐ ๋ฌธ์ž์—ด์„ ํด๋ผ์ด์–ธํŠธ์— ๋ณด๋‚ธ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋Š” ์‚ฌ์šฉ์ž ์ •๋ณด์™€ nonce๋ฅผ ํฌํ•จํ•˜๋Š” ํ•ด์‹œ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ ์‘๋‹ตํ•œ๋‹ค (RFC 7616)

HOBA

์ „์ž ์„œ๋ช… ๊ธฐ๋ฐ˜ ์ธ์ฆ (RFC 7486)

Mutual

์•”ํ˜ธ๋ฅผ ์ด์šฉํ•œ ํด๋ผ์ด์–ธํŠธ-์„œ๋ฒ„ ์ƒํ˜ธ ์ธ์ฆ (draft-ietf-httpauth-mutual)

AWS4-HMAC-SHA256

AWS ์ „์ž ์„œ๋ช… ๊ธฐ๋ฐ˜ ์ธ์ฆ (๋งํฌ)

 

์œ„ ์ธ์ฆ ํƒ€์ž…์˜ ์ข…๋ฅ˜์—์„œ ๋‚˜ํƒ€๋‚œ ๊ฒƒ ์ฒ˜๋Ÿผ, bearer๋Š” JWT์™€ OAuth๋ฅผ ํƒ€๋‚˜๋‚ด๋Š” ์ธ์ฆ ํƒ€์ž…์ž…๋‹ˆ๋‹ค.

 

 

์•„๋ž˜๋Š” ์ž์„ธํ•œ ์„ค๋ช…

  • Basic Auth - It is the simplest type of auth header built over HTTP protocol. The header features the word Basic and a base 64-encoded string username. 

Here is an example header:

Authorization: Basic U2hpdmFuc2hpOnNkZmY=
  • Bearer Token - It involves the processing of bearer tokens that are server-generated cryptic strings. This token-based method may use various encryption algorithms, such as: RS512, RS384, ES256, etc.

The format is:

Authorization: Bearer <token>

Input Example:

 

Output Example:

Authorization:

Bearer eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyIiwibmFtZSI6IlNoaXZhbnNoaSIsImFkbWluIjp0cnVlLCJpYXQiOjE1MTYyMzkwMjJ9.Oajdup5xN4ldNZ8aP-9N3aJobyKa-DymD1freJOzJhigHOKmwWdpJ4vzrd2lvnGT_k-uIet79DVq4nrsLfZex6rfcs7p9vw4WgyfS5AdCKveisRoaz-7JXXF5FJOM6Twz75il7TVUw2nVVthCG4xWyN-noruvbLrn_HVK4zCO-w7lx7TnWD0epuYb3uGq3Dnb4YZIAD_-8B_k18juCUnemOIkaHt3CrcTuqp2gxgBkhSMoR2zm1oBlk-gYzKvfQRWGArIkzUaevtbq8_XYPXBOHb8YFfsVHD6lnloNYmfNRrtg8aoTaTvspk03rIVCy7gTypEWlKr-elJzUHSaW9gA

 

 

  • API Key - It is the client-generated tokens processed when API calls are made. In this type of authentication, the end-user shares a key-value pair to API as request header or query parameters. 

In general, this key is passed in URL as GET or POST request, and is in string format. 

Example:

GET /endpoint?api_key=gjukghl121264354354864

In request header, the same key can be passed as

X-API-Key: gjukghl121264354354864
  • Digest Auth - This type of authentication passes on the user information in a highly encrypted form. The encryption is achieved by applying the hash algorithm to the login credentials. 

For the above example, we can post the following line in the .htdigest file:

demo:hello:4433cbdf49dae47093f59231504917fb
  • OAuth 2.0 - It is an inventive basic authorization and precedes OAuth 1.0. It involves retrieving the access token for the APIs and using them further to verify the further requests made. 

Example:

 

  • Hawk Authentication - It takes the help of cryptographic verification to authorize the access requests. 

Example:

Authorization:

Hawk id="user123", ts="1546300800", nonce="gWqbkw", mac="4433cbdf49dae47093f59231504917fb/OnNkZmY="
  • AWS Signature - It works only for AWS requests and involves using a customized HMAC HTTP scheme for user’s identity verification. 

Example:

AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=host;range;x-amz-content-sha256;x-amz-date,Signature=f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bd

 

๋” ๋‚˜์•„๊ฐ€ CSRF ๋ž€?

Cross-Site Request Forgery ๋ฒˆ์—ญํ•˜์ž๋ฉด ์‚ฌ์ดํŠธ ๊ฐ„ ์š”์ฒญ ์œ„์กฐ.

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ทจ์•ฝ์  ์ค‘ ํ•˜๋‚˜๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์ž์‹ ์˜ ์˜์ง€์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๊ณต๊ฒฉ์ž๊ฐ€ ์˜๋„ํ•œ ํ–‰๋™์„ ํ•ด์„œ ํŠน์ • ์›นํŽ˜์ด์ง€๋ฅผ ๋ณด์•ˆ์— ์ทจ์•ฝํ•˜๊ฒŒ ํ•œ๋‹ค๊ฑฐ๋‚˜ ์ˆ˜์ •, ์‚ญ์ œ ๋“ฑ์˜ ์ž‘์—…์„ ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ณต๊ฒฉ ๋ฐฉ๋ฒ•์ด๋‹ค. 2008๋…„์— ๋ฐœ์ƒํ•œ ์˜ฅ์…˜์˜ ๊ฐœ์ธ์ •๋ณด ์œ ์ถœ ์‚ฌ๊ฑด์—์„œ๋„ ๊ด€๋ฆฌ์ž ๊ณ„์ •์„ ํƒˆ์ทจํ•˜๋Š” ๋ฐ ์ด ๋ฐฉ๋ฒ•์ด ์‚ฌ์šฉ๋˜์—ˆ๋‹ค. ๊ณต๊ฒฉ์˜ ๋‚œ์ด๋„๊ฐ€ ๋†’์ง€ ์•Š์•„ ํ”ํžˆ ์‚ฌ์šฉ๋œ๋‹ค.
 
 
 

SESSIONID ์‚ฌ์šฉํ•˜๊ธฐ

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋Š” in-memory database๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— 

์„œ๋ฒ„๋ฅผ ๊ป๋‹ค๊ฐ€ ์ผ  ํ›„์— 

๋ธŒ๋ผ์šฐ์ €์—์„œ refresh ํ•˜๋ฉด ๊ธฐ์กด์— ๋กœ๊ทธ์ธ ๋˜์–ด์žˆ๋˜ ์œ ์ €๊ฐ€ ๋กœ๊ทธ์•„์›ƒ ๋˜๋ฉด์„œ 

์ƒˆ๋กœ ๋กœ๊ทธ์ธ์„ ํ•ด์•ผํ•˜๊ณ 

๋กœ๊ทธ์ธ ์‹œ์— ์ƒˆ๋กœ์šด JSESSIONID ๊ฐ€ ๋ฐœ๊ธ‰๋œ๋‹ค.

 

 

 

Reference : https://devuna.tistory.com/59