UserDetails 이란?

Spring Security에서 사용자의 정보를 담는 인터페이스이다.

 

 

  • 시큐리티 로그인 과정 

시큐리티가 로그인 요청을 가로채어 시큐리티 세션에 username, password 를 저장하고 

로그인 완료하면 세션에 있는 유저정보 객체를 UserDetails 타입을 가지고있는 객체를 따로만들어 저장시켜준다. 

▶ implements UserDetails

 

 

 

먼저 시큐리티 로그인을 할때는 form태그로 submit을 한다. 

 

로그인페이지

[loginProc] 

<form action="/auth/loginProc" method="post">
    <div class="form-group">
        <label for="username">UserName : </label> <input type="text"  name="username" class="form-control" placeholder="Enter UserName" id="username">
    </div>

    <div class="form-group">
        <label for="pwd">Password : </label> <input type="password"  name="password" class="form-control" placeholder="Enter password" id="password">
    </div>

    <button id="btn-login" class="btn btn-primary">로그인 완료</button>
</form>

username,password의 name 속성값을 가지고 <form action ="/auth/loginProc" method="post"> 주소값으로 가도록 한다.

 

 

로그인 요청을 시큐리티가 가로챌거라서 Controller에서 /auth/loginProc 주소로 된 Post매핑을 안만들것이다 

시큐리티가 로그인 요청을 어떻게 가로채느냐?

 

 

 

[SecurityConfig]

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable() // csrf 토큰 비활성화 (테스트시 걸어두는게 좋음)
        .authorizeRequests()
            .antMatchers("/","/auth/**", "/js/**", "/css/**","/image/**")
            .permitAll()
            .anyRequest() // 다른 요청들은
            .authenticated()// 인증이 되어야한다.
        .and()
            .formLogin()
            .loginPage("/auth/loginForm")
            .loginProcessingUrl("/auth/loginProc")  // 스프링 시큐리티가 해당 주소로 오는 로그인 요청을 가로채어 로그인한다.
            .defaultSuccessUrl("/"); // 정상적으로 요청이 완료되면 "/" 주소로 이동
}

 

+ 추가하기 

  • .loginProcessingUrl("/auth/loginProc")  스프링 시큐리티가 해당 주소로 오는 로그인 요청을 가로채어 대신 로그인
  • .defaultSuccessUrl("/");   = 정상적으로 요청이 완료되면 "/" 주소로 이동

 

인증이 되지않은 요청은 ▶ .loginPage("/auth/loginForm") 로 이동하고 

로그인을 수행하고 '로그인완료' 버튼을 누르면 ▶ .loginProcessingUrl("/auth/loginProc") 

스프링 시큐리티가 로그인 요청을 가로채 로그인을 한다 

로그인이 정상적으로 완료가 되면 ▶ .defaultSuccessUrl("/"); 주소로 이동

 

** .failureUrl("/fail") : 실패시 해당 주소로 가겠다 (나는 적용 안함)

 

 

 

☆ 정리

스프링시큐리티가 username, password를 가로채어 로그인 완료가 되면 시큐리티 세션에 유저정보를 저장하고 관리를하는데 시큐리티 세션에 있는 유저정보 객체는 타입이 정해져있어서

기존의 user 객체가 아닌 UserDetails 타입을 가지고 있는 유저obj를 만들어줘야한다.

 

 

 

 

auth 패키지안에 [ PrincipalDetail ]

  1. implements UserDetails : 추상메서드를 들고있는 UserDetails 를 모두 오버라이드 해야한다.
  2. alt+shift+s 하여 오버라이딩 한다.
public class PrincipalDetail implements UserDetails{
	private User user; // 콤포지션(객체를 들고 있는)

	//생성자
	public PrincipalDetail(User user) {
		this.user = user;
	}
	
	//alt+shift+s
	@Override
	public String getPassword() {
		return user.getPassword();
	}

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

	//계정이 만료되지 않았는지 리턴한다. ( true : 만료안됨)
	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	//계정이 감져있지 않았는지 리턴한다. ( true : 잠기지 않음)
	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	//비밀번호가 만료되지 않았는지 리턴한다. ( true : 만료안됨)
	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	//계정 활성화(사용가능)인지 리턴한다. ( true : 활성화)
	@Override
	public boolean isEnabled() {
		return true;
	}
	
	//계정이 갖고있는 권한 목록을 리턴한다. (권한이 여러개 있을수있어서 루프를 돌아야 하는데  우리는 한개만)
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		
		Collection<GrantedAuthority> collectors = new ArrayList<>();
		collectors.add(()->{return "ROLE_"+user.getRole();}); //add에 들어올 파라미터는 GrantedAuthority밖에 없으니 
		
		return collectors;
	}
}

 

스프링 시큐리티가 로그인 요청을 가로채서 로그인을 진행-완료되면 UserDetails 타입의 오브젝트를 스프링 시큐리티의 고유한 세션저장소에 저장을 해준다.

즉 UserDetails 타입의 PrincipalDetail 이 저장이 된다. User 객체도 포함되어야 한다

 

 

 

 

마지막에 getAuthorities() 함수 람다식을 쓰면서 생략한 과정을 상세히 설명

 

계정이 갖고있는 권한 목록을 리턴하는 getAuthorities() 함수는 타입이 Collection이고 GrantedAuthority을 상속

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {

    Collection<GrantedAuthority> collectors = new ArrayList<>();
    collectors.add(new GrantedAuthority() {

        @Override
        public String getAuthority() {
            return "ROLE_"+user.getRole();   // ROLE_USER
        }
    });

    return collectors;
}
}

getAuthority() 함수를 넣기위해  new GrantedAuthority로 감싸준건데

 

자바는 new GrantedAuthority 객체를 담을 수 있어도 아래처럼 getAuthority() 메서드를 넘길순 없다!

collectors.add(
        @Override
        public String getAuthority() {
            return "ROLE_"+user.getRole();  
        }
    });

 

 

위를 람다식으로 쉽게 표현해보자 

 

 

  • 람다식을 쓰면서 생략할수 있는것

1. collectors.add() 함수 안에 들어올 파라미터는 new GrantedAuthority 밖에 없기 때문에 안적어도 알수있고

2. GrantedAuthority 소스파일을 보면(ctrl+우클릭) getAuthority() 메소드 하나밖에 안들고 있어서 안적고 리턴값만 쓴다 

 

 collectors.add(new GrantedAuthority() {  

        @Override
        public String getAuthority() {
            return "ROLE_"+user.getRole();  
        }
    });

return "ROLE_"+user.getRole();  만 남는다.  이것을 람다식으로 하면 ()→{}

  collectors.add(()->{return "ROLE_"+user.getRole();}); 

 

 

 

람다식 결과코드

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {

Collection<GrantedAuthority> collectors = new ArrayList<>();
   collectors.add(()->{return "ROLE_"+user.getRole();}); // ROLE_USER
   return collectors;
}

☆ ROLE_ 로 시작을 해야 정상작동한다. ex) ROLE_USER / ROLE_ADMIN

 

 

원래는 권한이 여러개 있을수있어서 for문을 돌아야 하는데  우리는 한개만있어서 .add() 함수로 리턴함.

 

 

 

반응형
LIST

+ Recent posts