728x90

로그인 후 그 객체의 사용법을 정리해보자.

1. Bean을 통해 가져오기

SecurityContextHolder를 통해 가져온다.

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 
UserDetails userDetails = (UserDetails)principal; 
String username = principal.getUsername(); 
String password = principal.getPassword();

 

2. Controller에서 사용자 정보 얻기

Principal 객체에 접근해서 정보를 가져온다.

    @GetMapping("/list")
    public String test(Model model, Principal principal){
        String loginUsername = principal.getName();        
        model.addAttribute("loginUsername", loginUsername);
        
        return "board/list";
    }

 

-

 

출처 : https://djunnni.gitbook.io/springboot/2019-11-30

 

추가 : 로그인 초기화 (회원탈퇴 등에 사용)

SecurityContextHolder.clearContext();
728x90

작업의 루틴은 다음과 같다.

  1. 시큐리티가 로그인을 진행시키면 UserDetails타입의 객체를 실행한다.
  2. 로그인 진행이 완료되면 시큐리티 session을 생성하고 Security ContextHolder라는 키 값으로 세션 정보 저장한다.
  3. 키에 대한 값으로 들어가는 오브젝트 타입은 Authentication이다.
  4. Authentication 안에는 User(UserDetails타입) 정보가 있어야 됨.

1. 시큐리티가 로그인을 진행시키면 실행 될 UserDetails타입의 클래스를 생성하기 위해 UserDetails를 implements한다.

public class PrincipalDetails implements UserDetails {

    private Member member;

    public PrincipalDetails(Member member) {
        this.member = member;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> collection = new ArrayList<>();
        collection.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                return member.getRole();
            }
        });
        return collection;
    }

    @Override
    public String getPassword() {
        return member.getPassword();
    }

    @Override
    public String getUsername() {
        return member.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
  • getAuthorities() : 해당 유저의 권한을 리턴한다. 리턴 타입이 GrantedAuthority이므로 Collection을 이용해서 반환 타입을 설정
  • isAccountNonExpired() : 계정이 만료됐는지 체크
  • isCredentialsNonExpired() : 비밀번호를 오래 사용했는지 체크
  • isEnabled() : 계정의 활성화 여부를 체크

2. 로그인 요청이 오면 자동으로 UserDetailsService 타입으로 IoC되어 있는 loadUserByUsername 함수가 실행되므로 UserDetailsService를 implements하는 서비스 클래스 생성

@Service
public class PrincipalDetailsService implements UserDetailsService {

    @Autowired
    private JpaMemberRepository jpaMemberRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Member memberEntity = jpaMemberRepository.findByUsername(username);

        if(memberEntity != null) {
            return new PrincipalDetails(memberEntity);
        }
        return null;
    }
}
  • 파라미터로 받는 username은 form에서 받는 input태그와 name이 같아야 한다.
  • 다를 경우 시큐리티 config 파일에서 수정할 수 있다. (userNameParameter()이용)

결국 Security Session 안 -> Authentication객체 안 -> UserDetails(PrincipalDetails) 형식이 완성되고 시큐리티에 의해 로그인이 완료된다.

 

-

 

* 해당 글은 인프런에 게시된 '최주호'님의 강의를 듣고 개인적으로 정리한 글입니다.

강의 출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0

728x90

1. 의존성 추가

 - build.gradle 에 다음 의존성을 추가해준다. 

 

implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-test'

 

 

 - Maven의 경우 pom.xml 파일에 다음을 추가한다.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-test</artifactId>
  <scope>test</scope>
</dependency>

2. 시큐어Configure 파일을 생성해서 다음의 코드를 작성해준다.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http
                .authorizeRequests()
                    .antMatchers("/css/**").permitAll()
                    .antMatchers("/board/**").authenticated()
                    .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") // 권한 설정
                    .anyRequest().permitAll() 
                    .and()
                .formLogin()
                    .loginPage("/loginForm") // 로그인 페이지
                    .loginProcessingUrl("/login")
                    .defaultSuccessUrl("/")
                    .and()
                .logout()
                    .permitAll();
    }
}
  • @EnableWebSecurity : 스프링 시큐리티 필터가 스프링 필터체인에 등록된다.
  • BCryptPasswordEncoder : 유저의 패스워드를 암호화하는 인코더
  • authorizeRequests() : 시큐리티 처리에 HttpServletRequest를 이용하겠다는 의미
  • antMatchers() : 특정 경로 지정
  • permitAll() : 접근 허용
  • authenticated() : authorizeRequests에 적힌 경로는 로그인 후에 접근 가능
  • access : 주어진 SpEL표현식의 결과가 true이면 접근 허용
  • hasRole('ROLE_ADMIN') : 권한이 ROLE_ADMIN인 경우만 접근 가능
  • anyRequest().permitAll() : 그 외 요청은 허용
  • loginProcessingUrl("/login") : 시큐리티에서 로그인을 대신 진행하도록 정의 -> 컨트롤러에 안만들어도 됨.

 

3. 비밀번호 인코딩

@PostMapping("/join")
public String join(Member member){ // view의 form->input 의 name과 매핑됨.
    String encPwd = bCryptPasswordEncoder.encode(member.getPassword());
    member.setPassword(encPwd);

    memberService.join(member);

    return "redirect:/loginForm";
}
  • 컨트롤러에서 BCryptPasswordEncoder 객체를 이용해서 encode 함수를 통해 인코딩 한다.

-

 

 

* 해당 글은 인프런에 게시된 '최주호'님의 강의를 듣고 개인적으로 정리한 글입니다.

강의 출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0

+ Recent posts