[Spring Security] 4. Spring Security 로그인, 권한처리

개발자·2022년 5월 2일
2

Spring Security

목록 보기
4/11
post-thumbnail

로그인 구현

로그인을 구현하기위해서 auth 패키지를 생성한다.
auth 패키지는 Spring Security가 로그인을 진행한다. 그 때 로그인이 완료가 되면 Security Session을 만들어준다 ( security contextholder 안에 저장)

이 Session 에 들어가는 정보 Object 가 정해져 있는데 이것이 바로 Authentication 객체여야 한다.

Authentication 안에는 User 정보가 있어야 한다 -> User Object의 type은 UserDetails type 객체여야한다.

즉 Spring Security => Authentication 객체 => UserDetails 객체
이렇게 있다는 뜻이다.

PrincipalDetails.java

package com.cos.securiy1.auth;

import com.cos.securiy1.model.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

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

public class PrincipalDetails implements UserDetails {

    private User user; // 컴포지션

    public PrincipalDetails(User user){
        this.user = user;
    }

    // 해당 User의 권한을 리턴하는곳.
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> collect = new ArrayList<>();
        collect.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                return user.getRole();
            }
        });
        return null;
    }


    // User 의 password 리턴
    @Override
    public String getPassword() {
        return user.getPassword();
    }

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

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

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

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

    @Override
    public boolean isEnabled() {

        // 사이트 내에서 1년동안 로그인을 안하면 휴먼계정을 전환을 하도록 하겠다.
        // -> loginDate 타입을 모아놨다가 이 값을 false로 return 해버리면 된다.
        return true;
    }
}
  • 다음과 같이 PrincipalDetails class 를 정의하였다.
  • PrincipalDetails 타입은 userDetails 타입을 상속받아서 정의한다.
  • 이렇게 된 PrincipalDetails 타입을 Authentication 객체안에 넣는다.
  • 컴포지션을 통해서 User 객체를 만들고 생성자를 통해서 받는다.
  • UserDetails에 정의된 함수를 오버라이드하여 정의해준다.

PrincipalDetailsService.java

package com.cos.securiy1.auth;

import com.cos.securiy1.model.User;
import com.cos.securiy1.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
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;

@Service
public class PrincipalDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User userEntity = userRepository.findByUsername(username);
        if(userEntity != null){
            return new PrincipalDetails(userEntity);
        }
        return null;
    }
}
  • Authentication 객체를 생성하기 위해서 PrincipalDetailsService class 를 생성한다.
  • UserDetailsService를 상속하여 정의한다.
  • @Service 어노테이션을 통해서 메모리에 띄운다
  • 로그인 요청이 오면 자동으로 loadUserByUsername 함수가 실행되도록 정의되어있다.
  • 이 때 매개변수로 받는 String username은 html에서 정의된 input type의 name 과 동일하여야 한다.
  • 이렇게 찾는 Username이 있으면 유저 객체를 반환해주고 없으면 null을 반환하여 로그인을 하게 해준다.

UserRepository.java

package com.cos.securiy1.repository;

import com.cos.securiy1.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

// CRUD 함수를 들고있음
// @Repository 어노테이션이 없어도 IOC가 된다.
public interface UserRepository extends JpaRepository<User,Integer> {
    // findBy규칙 -> Username 문법
    // select * from user where username = ?
    public User findByUsername(String username);
}
  • 다음과 같이 Username으로 User를 찾는 함수를 정의하여준다.

권한처리

User의 정보에는 ROLE 이라는 변수가 있다.
권한에 따라서 사용자가 들어갈 수 있는 페이지를 처리하는것이다.

테스트를 위해 MySQL에서 일부 유저를 "ROLE_MANAGER","ROLE_ADMIN"으로 수정한다.

이렇게 하면 기존에 설정했던 /manager , /admin 페이지를 접근할 수 있는 권한이 생기는 것이다.

이 때, 기존에 SecurityConfig에 설정해 놓지 않은 주소들 또한 권한처리를 할수 있다.

@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) 

다음과 같은 Annotation을 SecurityConfig에 추가한다.
그렇게하면

  • secured 어노테이션 활성화
  • preAuthorize/postAuthorize 어노테이션 활성화

다음과 같은 어노테이션을 사용할 수 있다.

사용 방법은 아래와 같이 하면 된다.

    @Secured("ROLE_ADMIN")
    @GetMapping("/info")
    public @ResponseBody String info(){
        return "개인정보";
    }

    @PreAuthorize("hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')")
    @GetMapping("/data")
    public @ResponseBody String data(){
        return "데이터정보";
    }
  • @Secured Annotation은 간단하게 걸고싶을때 (하나의 권한만 걸 때 사용)
  • @PreAuthorize Annotation은 두개이상의 권한을 줄 수 있다
  • @PreAuthorize는 메소드 실행 전에, @PostAuthorize는 메소드 실행 후에 권한처리를 하는것인데 @PostAuthorize는 최근에 사용하지 않고 있다.

마무리

다음과 같이 Security 에서 로그인을 처리하고 권한을 처리하는 방법을 해보았다.

다음에는 OAuth2를 이용하여 기존 로그인 뿐 만 아니라 구글,페이스북,네이버 같은 다른 사이트에서 로그인하는것을 해보려고 한다!

0개의 댓글