헤르메스 LIFE

[Spring Boot] Spring Security #02 본문

Spring Boot Framework

[Spring Boot] Spring Security #02

헤르메스의날개 2022. 3. 2. 23:31
728x90

springboot.z01
15.00MB
springboot.z02
15.00MB
springboot.zip
5.58MB

개발환경

1. STS 버전 : 4.13.1

2. JDK 버전 : OpenJDK 11.0.14_9_x64

3. Tomcat 버전 : 9.0.56

4. Maven 버전 : 3.8.4

5. Spring 버전 : Spring Boot 2.6.3

6. Database : Docker 에 DB 설치

- primary - PostgreSQL 13.3

7. Spring Security : 5.6.1

8. lombok


목표

1. Spring Boot 환경에서 Spring Security 기능을 추가하여 로그인 기능을 완성

2. 인프런의 최주호 강사님 스프링부트 시큐리티 & JWT 강의를 듣고, 그 내용을 구현


https://hermeslog.tistory.com/583?category=1078420 

 

[Spring Boot] Spring Security #01

개발환경 1. STS 버전 : 4.13.1 2. JDK 버전 : OpenJDK 11.0.14_9_x64 3. Tomcat 버전 : 9.0.56 4. Maven 버전 : 3.8.4 5. Spring 버전 : Spring Boot 2.6.3 6. Database : Docker 에 DB 설치 - primary - Postgre..

hermeslog.tistory.com


User.java -> Member 테이블을 생성하게 됩니다.

Postgresql은 User 테이블이 시스템 테이블인 듯 합니다.

package com.study.springboot.system.entity;

import java.sql.Timestamp;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import org.hibernate.annotations.CreationTimestamp;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity(name="member")
@NoArgsConstructor
@Getter
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long      id;
    private String    username;
    private String    password;
    private String    email;
    private String    role;          // ROLE_USER, ROLE_ADMIN
    @CreationTimestamp
    private Timestamp createDate;

    /**
     * @param username
     * @param password
     */
    public User(String username, String password) {
        this.username   = username;
        this.password   = password;
    }
    
    /**
     * @param username
     * @param password
     */
    public User(String username, String password, String email) {
        this.username   = username;
        this.password   = password;
        this.email      = email;
    }

    /**
     * @param username
     * @param password
     */
    public User(String username, String password, String email, String role) {
        this.username   = username;
        this.password   = password;
        this.email      = email;
        this.role       = role;
    }

    public void setRole( String role ) {
        this.role = role;
    }

    
    
}

PrincipalDetails.java

package com.study.springboot.config.auth;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.study.springboot.system.entity.User;

/**
 * Spring Security가 /login.do 요청이 들어오면
 * 로그인이 완료되면 Security Session 을 생성한다. ( SecurityHolder )
 * Object Type => Authentication 타입 객체
 * Authentication 안에 User 정보가 있어야 함.
 * User Object Type => UserDetails Type 객체
 * 
 * Security Session -> Authentication -> UserDetails(PrincipalDetails)
 */
@SuppressWarnings("serial")
public class PrincipalDetails implements UserDetails {
    private User user; // 콤포지션

    // 생성자 만들기
    public PrincipalDetails(User user) {
        this.user = user;
    }

    @Override // 사용자의 비밀번호를 알고 싶으면 호출
    public String getPassword() {
        return user.getPassword();
    }

    @Override // 사용자의 유저네임를 알고 싶으면 호출
    public String getUsername() {
        return user.getUsername();
    }

    @Override // 사용자가 만료된 지를 알고 싶으면 호출
    public boolean isAccountNonExpired() { // 만료안됐니?
        // 접속시간확인하여 true false 리턴
        return true;
    }

    @Override
    public boolean isAccountNonLocked() { // 락 안걸렸니?
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        // TODO Auto-generated method stub
        return true;
    }

    @Override
    public boolean isEnabled() { // 계정활성화 되어있니?
        return true;
    }

    // Arrays.asList(new SimpleGrantedAuthority(user.getRole()));
    @Override // 어떤 권한을 가졌니?
    public Collection<? extends GrantedAuthority> getAuthorities() {

        Collection<GrantedAuthority> authList = new ArrayList<>();
        authList.add(new SimpleGrantedAuthority(user.getRole()));
        return authList;

    }
}

WebMvcConfig.java

package com.study.springboot.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.study.springboot.config.interceptor.AccessInterceptor;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    /**
     * <pre>
     * Interceptor 설정
     * excludePathPatterns 설정이 변경되면, SecurityConfig Class도 변경되어야 한다.
     * </pre>
     */
    @Override
    public void addInterceptors( InterceptorRegistry registry ) {
        registry.addInterceptor(new AccessInterceptor()).addPathPatterns("/**") // 해당 경로에 접근하기 전에 인터셉터가 가로챈다.
                .excludePathPatterns("/css/**")             // 해당 경로는 인터셉터가 가로채지 않는다.
                .excludePathPatterns("/fonts/**")           // 해당 경로는 인터셉터가 가로채지 않는다.
                .excludePathPatterns("/images/**")          // 해당 경로는 인터셉터가 가로채지 않는다.
                .excludePathPatterns("/js/**")              // 해당 경로는 인터셉터가 가로채지 않는다.
                .excludePathPatterns("/modules/**")         // 해당 경로는 인터셉터가 가로채지 않는다.
                .excludePathPatterns("/error")              // 해당 경로는 인터셉터가 가로채지 않는다.
                .excludePathPatterns("/login.do")           // 해당 경로는 인터셉터가 가로채지 않는다.
                .excludePathPatterns("/logout.do")          // 해당 경로는 인터셉터가 가로채지 않는다.
                .excludePathPatterns("/upload.do");         // 해당 경로는 인터셉터가 가로채지 않는다.
    }

}

SecurityConfig.java

package com.study.springboot.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.study.springboot.config.auth.CustomAuthenticationSuccessHandler;

@Configuration
@EnableWebSecurity     // 스프링 Security Filter가 Spring Fileter Chain에 등록된다.
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);

    @Bean
    public BCryptPasswordEncoder encodePwd() {
        return new BCryptPasswordEncoder();
    }

    /**
     * <pre>
     * Security 무시 설정
     * excludePathPatterns 설정이 변경되면, WebMvcConfig Class도 변경되어야 한다.
     * </pre>
     */
    @Override
    public void configure( WebSecurity web ) {
        web.ignoring().antMatchers("/css/**", "/fonts/**", "/images/**", "/js/**", "/modules/**")
                .antMatchers("/h2-console/**", "/swagger-ui/**");
    }

    @Override
    protected void configure( HttpSecurity http ) throws Exception {
        // 해당 기능을 사용하기 위해서는 프론트단에서 csrf토큰값 보내줘야함
        // <input type="hidden" name="${_csrf.paremeterName }" value="${_csrf.token }"/>
        http.csrf().disable(); // Spring Security의 SCRF를 막음. Post가 안될 경우가 존재하면 막는 경우도 있음.

        http.authorizeHttpRequests() //
                .antMatchers("/login.do", "/logout.do", "/swagger-ui/**", "/swagger-ui").permitAll() // 로그인 하지 않고 모두 권한을
                                                                                                     // 가짐.
                .anyRequest().authenticated() // 그 외 모든 요청은 인증된 사용자만 접근 가능
        // .anyRequest().permitAll() // 로그인 하지 않고 모두 권한을 가짐.
        ;

        http.requiresChannel()
                // .antMatchers("/**").requiresSecure() // https 로 리다이렉스 시킴
                .antMatchers("/**").requiresInsecure() // http 로 리다이렉스 시킴
        ;

        http.formLogin() // 로그인 페이지와 기타 로그인 처리 및 성공 실패 처리를 사용하겠다는 의미 입니다.
                .loginPage("/login.do")                          // Login 화면
                .loginProcessingUrl("/loginProcess.do")          // Login 프로세스
                // .defaultSuccessUrl("/main.do", true)
                .successHandler(new CustomAuthenticationSuccessHandler("/main.do")) // 인증에 성공하면 Main 페이지로 Redirect
                // // .failureHandler(new CustomAuthenticationFailureHandler("/login-fail")) // 커스텀 핸들러를 생성하여 등록하면 인증실패
                // 후
                .failureUrl("/login.do?fail=true") // 인증이 실패 했을 경우 이동하는 페이지를 설정합니다.
                .usernameParameter("userId")                    // Login ID 명칭지정 - MemberRepository 의 id와 매칭됨.
                .passwordParameter("password")                  // Login PW 명칭지정
        ;

        http.logout() //
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout.do")) // 로그아웃
                .logoutSuccessUrl("/login.do") // 로그아웃에 성공하면 페이지 Redirect
                .invalidateHttpSession(true) // Session 초기화
        ;
    }

}

CustomAuthenticationProvider.java

package com.study.springboot.config.auth;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private PrincipalDetailsService principalDetailsService;
    
    @Autowired
    private PasswordEncoder         passwordEncoder;

    @Override
    public Authentication authenticate( Authentication authentication ) throws AuthenticationException {
        logger.info("[CustomAuthenticationProvider] 시작 ---------------------------------");
        logger.info("authentication.getName() :: {}", authentication.getName().toString());
        logger.info("---------------------------------");

        PrincipalDetails principalDetails = (PrincipalDetails) principalDetailsService
                .loadUserByUsername(authentication.getName().toString());

        logger.info("principalDetails.getUsername :: {}", principalDetails.getUsername());
        logger.info("principalDetails.getPassword :: {}", principalDetails.getPassword());
        logger.info("principalDetails.getAuthorities :: {}", principalDetails.getAuthorities());
        
        logger.info("authentication.getCredentials() :: {}", authentication.getCredentials().toString());
        
        String reqPassword = authentication.getCredentials().toString();
        if ( !passwordEncoder.matches(reqPassword, principalDetails.getPassword()) )
            throw new BadCredentialsException("Not Found User");

        logger.info("[CustomAuthenticationProvider] 끝 ---------------------------------");
        
        return new UsernamePasswordAuthenticationToken(principalDetails, null, principalDetails.getAuthorities());
    }

    @Override
    public boolean supports( Class<?> authentication ) {
        return true;
    }
}

PrincipalDetailsService.java

package com.study.springboot.config.auth;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.study.springboot.system.entity.User;
import com.study.springboot.system.repository.MemberRepository;

// UserDetailsService는 IoC로 찾음
// /loginProcess.do 가 찾아오는 클래스임.
@Service // UserDetailsService타입으로 메모리에 뜬다 (덮어씌워짐)
public class PrincipalDetailsService implements UserDetailsService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private MemberRepository userRepository;

    @Override
    public UserDetails loadUserByUsername( String userId ) throws UsernameNotFoundException {
        // 어썬티케이션 매니저가 낚아챔
        // JPA는 기본적인 CRUD만 있어서 다른걸 쓰려면 만들어줘야함

        logger.info("[PrincipalDetailsService] userId :: {}", userId);

        User user = userRepository.findById(Long.parseLong(userId));

        logger.info("[PrincipalDetailsService] user :: {}", user);

        if ( user != null ) {
            List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
            String                 roles[]     = user.getRole().split(",");
            for ( int i = 0; i < roles.length; i++ ) {
                authorities.add(new SimpleGrantedAuthority(roles[i]));
            }

            return new PrincipalDetails(user);  // SecurityContext의 Authertication에 등록되어 인증정보를 가진다.
        }

        return null;
    }
}

MemberRepository.java

package com.study.springboot.system.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.study.springboot.system.entity.User;

@Repository
public interface MemberRepository extends CrudRepository<User, Long> {
    // Jpa Naming 전략
    // SELECT * FROM member WHERE id = ?
    // Insert into member ( password, username, createdate, email, role) values ( '$2a$10$C4BZPH4raAlKGrvy9dtyyufBp56af2W6fge0hD1wLctWvNEjrK.AG', '홍길동', now(), 'hermeswing@test.com', 'ROLE_ADMIN')
    // id:1, pw:1234
    User findById( long userId ); // JPA Query Method
}

CustomAuthenticationSuccessHandler.java

package com.study.springboot.config.auth;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private String redirectUri;

    public CustomAuthenticationSuccessHandler(String redirectUri) {
        this.redirectUri = redirectUri;
    }

    @Override
    public void onAuthenticationSuccess( HttpServletRequest request, HttpServletResponse response,
            Authentication authentication ) throws IOException, ServletException {

        logger.info("[CustomAuthenticationSuccessHandler] 인증성공 ==============================================================");

        logger.info("1. 사용자 Session 생성");
        
        HttpSession session = request.getSession(true);
        session.setAttribute("UserDetail", authentication.getCredentials());

        response.sendRedirect(redirectUri);

        // 에러 세션 지우기
        clearAuthenticationAttributes(request);

        logger.info("session 정보 :: {}", session.getId());
        logger.info("UserDetail :: {}", session.getAttribute("UserDetail"));
        logger.info("[CustomAuthenticationSuccessHandler] 끝 ==============================================================");
    }

    // 남아있는 에러세션이 있다면 지워준다.
    protected void clearAuthenticationAttributes( HttpServletRequest request ) {
        HttpSession session = request.getSession(false);
        if ( session == null ) return;

        session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
    }

}

AccessInterceptor.java

package com.study.springboot.config.interceptor;

import java.net.URI;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.UriComponentsBuilder;

public class AccessInterceptor implements HandlerInterceptor {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 컨트롤러에 진입하기 전에 실행됩니다.
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        logger.info("[AccessInterceptor] 시작 ==============================================================");

        String url = request.getRequestURI();

        URI uri = UriComponentsBuilder.fromUriString(request.getRequestURI()).query(request.getQueryString()).build()
                .toUri();
        int httpStatus = response.getStatus();
        
        logger.info("AccessInterceptor preHandle >> url :: {} >> {}", url, httpStatus);
        logger.info("AccessInterceptor preHandle >> uri :: {} >> {}", uri, httpStatus);

        logger.info("1. 접근 URL 체크");

        // HttpSession session = request.getSession();
        // UserVO loginVO = (UserVO) session.getAttribute("loginUser");
        //
        // if (ObjectUtils.isEmpty(loginVO)) {
        // response.sendRedirect("/moveLogin.go");
        // return false;
        // } else {
        // session.setMaxInactiveInterval(30 * 60);
        // return true;
        // }

        logger.info("[AccessInterceptor] 끝 ==============================================================");

        return true;

    }

    /**
     * 컨트롤러 진입 후 View가 랜더링 되기 전에 수행됩니다.
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        logger.info("AccessInterceptor postHandle");
    }

    /**
     * 컨트롤러 진입 후 view가 랜더링 된 후에 실행되는 메소드입니다.
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        logger.info("AccessInterceptor afterCompletion");
    }

}

Email Address -> User ID 로 변경 했습니다.

로그인 하는 동안의 Log

23:00:03.097 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationProvider - [CustomAuthenticationProvider] 시작 ---------------------------------
23:00:03.097 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationProvider - authentication.getName() :: 2511
23:00:03.097 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationProvider - ---------------------------------
23:00:03.097 [http-nio-9090-exec-8] INFO  c.s.s.c.auth.PrincipalDetailsService - [PrincipalDetailsService] userId :: 2511
23:00:03.099 [http-nio-9090-exec-8] DEBUG org.hibernate.SQL - select user0_.id as id1_1_0_, user0_.createDate as createda2_1_0_, user0_.email as email3_1_0_, user0_.password as password4_1_0_, user0_.role as role5_1_0_, user0_.username as username6_1_0_ from member user0_ where user0_.id=?
23:00:03.100 [http-nio-9090-exec-8] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - [2511]
23:00:03.101 [http-nio-9090-exec-8] TRACE o.h.t.descriptor.sql.BasicExtractor - extracted value ([createda2_1_0_] : [TIMESTAMP]) - [2022-03-01 14:01:26.729303]
23:00:03.101 [http-nio-9090-exec-8] TRACE o.h.t.descriptor.sql.BasicExtractor - extracted value ([email3_1_0_] : [VARCHAR]) - [hermeswing@test.com]
23:00:03.101 [http-nio-9090-exec-8] TRACE o.h.t.descriptor.sql.BasicExtractor - extracted value ([password4_1_0_] : [VARCHAR]) - [$2a$10$C4BZPH4raAlKGrvy9dtyyufBp56af2W6fge0hD1wLctWvNEjrK.AG]
23:00:03.101 [http-nio-9090-exec-8] TRACE o.h.t.descriptor.sql.BasicExtractor - extracted value ([role5_1_0_] : [VARCHAR]) - [ROLE_ADMIN]
23:00:03.101 [http-nio-9090-exec-8] TRACE o.h.t.descriptor.sql.BasicExtractor - extracted value ([username6_1_0_] : [VARCHAR]) - [홍길동]
23:00:03.102 [http-nio-9090-exec-8] INFO  c.s.s.c.auth.PrincipalDetailsService - [PrincipalDetailsService] user :: com.study.springboot.system.entity.User@26f4d238
23:00:03.102 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationProvider - principalDetails.getUsername :: 홍길동
23:00:03.102 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationProvider - principalDetails.getPassword :: $2a$10$C4BZPH4raAlKGrvy9dtyyufBp56af2W6fge0hD1wLctWvNEjrK.AG
23:00:03.102 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationProvider - principalDetails.getAuthorities :: [ROLE_ADMIN]
23:00:03.102 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationProvider - authentication.getCredentials() :: 1234
23:00:03.163 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationProvider - [CustomAuthenticationProvider] 끝 ---------------------------------
23:00:03.163 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationSuccessHandler - [CustomAuthenticationSuccessHandler] 인증성공 ==============================================================
23:00:03.163 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationSuccessHandler - 1. 사용자 Session 생성
23:00:03.163 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationSuccessHandler - session 정보 :: 8CD462D80262C18C529B571036062F60
23:00:03.164 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationSuccessHandler - UserDetail :: null
23:00:03.164 [http-nio-9090-exec-8] INFO  c.s.s.c.a.CustomAuthenticationSuccessHandler - [CustomAuthenticationSuccessHandler] 끝 ==============================================================
23:00:03.168 [http-nio-9090-exec-1] INFO  c.s.s.c.i.AccessInterceptor - [AccessInterceptor] 시작 ==============================================================
23:00:03.168 [http-nio-9090-exec-1] INFO  c.s.s.c.i.AccessInterceptor - AccessInterceptor preHandle >> url :: /main.do >> 200
23:00:03.168 [http-nio-9090-exec-1] INFO  c.s.s.c.i.AccessInterceptor - AccessInterceptor preHandle >> uri :: /main.do >> 200
23:00:03.168 [http-nio-9090-exec-1] INFO  c.s.s.c.i.AccessInterceptor - 1. 접근 URL 체크
23:00:03.168 [http-nio-9090-exec-1] INFO  c.s.s.c.i.AccessInterceptor - [AccessInterceptor] 끝 ==============================================================
23:00:03.169 [http-nio-9090-exec-1] INFO  c.s.s.c.i.AccessInterceptor - AccessInterceptor postHandle
23:00:03.171 [http-nio-9090-exec-1] INFO  c.s.s.c.i.AccessInterceptor - AccessInterceptor afterCompletion

로그인 성공한 모습 -> 실패하면 로그인 페이지로 이동합니다.


인프런의 최주호 강사님 스프링부트 시큐리티 & JWT 강의 에서 발췌했습니다.


참고

인프런의 최주호 강사님 스프링부트 시큐리티 & JWT 강의

https://jhhan009.tistory.com/31

 

Spring Security(2)

Spring Security에 관해 좀 더 실제 코드를 살펴 보겠습니다. @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http)..

jhhan009.tistory.com

https://kimchanjung.github.io/programming/2020/07/02/spring-security-02/

 

[Spring Security] 스프링시큐리티 설정값들의 역할과 설정방법(2)

스프링시큐리티의 여러가지 설정값들의 역할과 설정방법을 상세히 알아봅니다. Spring Security 커스텀 필터를 이용한 인증 구현 - 스프링시큐리티 설정(2) 본 포스팅은 스프링시큐리티의 전반적인

kimchanjung.github.io

https://devuna.tistory.com/59

 

[Spring Security] 스프링시큐리티 시작하기 /기본세팅

[Spring Security] 스프링시큐리티 시작하기 /기본세팅 💡 스프링 시큐리티 시작하기(기본세팅) 1. dependency 추가 org.springframework.security spring-security-web 4.2.2.RELEASE org.springframework.se..

devuna.tistory.com

 

728x90