security-context.xml에 다음과 같이 접근 제한을 설정한다.
롤 단위의 권한 부여 : 관리자 권한은 관리자 역할을 하는 사람에게 부여한다.
<security:http>
<security:intercept-url pattern="/sample/all" access="permitAll"/> <!-- 모든사람들에게 허용 -->
<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
<security:form-login/>
</security:http>
<security:authentication-manager>
</security:authentication-manager>
특정한 URI에 접근할 때 인터셉터를 이용해서 접근을 제한하는 설정은
<security:intercept-url>
을 이용한다.
pattern
속성 : URI의 패턴을 의미한다.
access
속성 : 권한을 체크한다.
이 페이지는 만든 적이 없는데 왜 나온거지? '/sample/member'를 접근하면 위와 같이 로그인 페이지로 강제 이동하는 것을 볼 수 있다.
모듈이 설치되어 제공되는 페이지이다. 시큐리티 모듈을 작성해서 넣은 결과이다. 핵심은 시큐리티 매니저가 인증과 권한부여에 대한 내부적 처리 수행함을 알 수 있다.
<security:authentication-manager>
에서 설정을 한다. 추가적인 설정을 통해서 지정된 아이디와 패스워드로 로그인이 가능하도록 설정을 추가한다. 패스워드는 암호화가 되어야 한다. 하지만 지금은 연습이므로 패스워드를 평문의 형태로 작성하도록 한다.
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="member"
password="member" authorities="ROLE_MEMBER"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
member는 ROLE_MEMBER라는 권한을 가진다는 의미이다.
member로 접근을 하면 허용이 되어야한다.
그런데 위에서 등록한 member를 입력하면 위와 같이 오류메세지가 뜬다.
왜 그럴까?
패스워드를 전달하기 위해서는 PasswordEncoder가 반드시 설정되어 있어야 한다. 패스워드가 있으면 패스워드에 특수한 함수를 돌리면 암호화된 문자열이 나온다. 이 암호화된 문자열을 SHA 알고리즘(해쉬함수)를 돌려서 나온다고 표현한다. 보통 128바이트로 암호화를 시킨다.
지금은, 패스워드를 인코딩하는 형태는 일방향이다.(쌍방향은 금지)
해쉬함수로 암호화를 하며, 128바이트 정도로 되어있다.(국제표준)
password="{noop}member"
이렇게 코드를 작성하면 인코딩을 하지말고 값을 전달하라는 뜻이다.
이제 로그인을 하면 회원만 볼 수 있는 페이지로 이동한다.
브라우저를 종료하고 다시 접속하면 로그인 페이지로 이동한다. 이는 이 역시 세션을 사용한다는 의미이다.
요청을 할때 Request Header부분에 쿠키를 같이 보낸다.
엣지에서 다시 요청을 하게되면 새로운 SessionId가 부여된다.
크롬에서는 기본적인 세팅이 세션이 공유되서 같은 페이지가 나오게 된다.
세션이 새로 만들어 질때
서버에서 세션공간이 사라질때
세션아이디는 브라우저를 종료하기 전까지 쿠키메모리에 저장되서 서버에 계속 보내질수 있다.
그럼 로그아웃은 어떻게 할까? 가장 무식한 방법은 Application에서 sessionID를 지우는 것이다. 그러나 이 방법은 서버 쪽에서는 sessionID 공간이 남아있다. 브라우저 상에서만 지워진 것이다.
클라이언트가 요청을 했지만, 권한이 없는 요청을 했다는 뜻이다.
<security:user name="admin" password="{noop}admin" authorities="ROLE_MEMBER, ROLE_ADMIN"/>
member에도 속하고 admin에도 속하게 코드를 작성한다.
특정한 사용자가 로그인은 했지만, URI를 접근할 수 있는 권한이 없는 상황이 발생할 수도 있다. 이 경우에는 접근 제한 에러 메시지를 보게 된다. member라는 권한을 가진 사용자는 '/sample/member'에는 접근할 수 있지만, '/sample/admin'은 접근할 수 없다. 이 경우에는 아래와 같은 메시지를 보게 된다.
이 Forbidden 페이지를 커스터마이징 할 수 있다. 스프링 시큐리티에서 접근 제한에 대해서 AccessDeniedHandler를 직접 구현하거나 특정한 URI를 지정할 수 있다.
<security:access-denied-handler error-page=""/>
위의 코드는 접근 제한이 일어나면 페이지를 이동한다는 의미이다.
코드를 작성할때 url이 나오면 이 페이지에 대한 컨트롤러가 당연히 추가 되어야 한다는것을 기억하자.
오류가 발생된 것의 인자가 위의 메서드에 들어간다!
@Controller
@Log4j
public class AccessDenyController {
@GetMapping("/accessError")
public void accessDenied(Authentication auth) {
log.info("인증 : " + auth);
}
}
위와 같이 코드를 작성한 후 member로 로그인 한 후 admin으로 접속하면 다음과 같은 내용이 콘솔에 뜬다.
INFO : com.zerock.controller.AccessDenyController - 인증 : org.springframework.security.authentication.UsernamePasswordAuthenticationToken@8f74b9: Principal: org.springframework.security.core.userdetails.User@bfc28a9a: Username: member; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_MEMBER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 1EBEA1261F4E81A76DDE161FC3E95D40; Granted Authorities: ROLE_MEMBER
jsp의 4단계 scope
Model은 request scope
getMessage() : 한줄로 간단하게
e.printstacktrace : 모든 정보를 다
만약 여기서 Model을 세션스코프로 바꾸면 어떻게 될까?
세션을 invalidate 하지 않는한 계속해서 유지해서 쓸수 있다.
위와 같이 jsp쪽에서 어느 스코프에서 찾을지 설정하면 시간을 단축할 수 있다.
<security:form-login login-page="/customLogin"/>
url을 지정하는 형태니까 컨트롤러가 받아야 한다.
LoginController 생성하고 기본틀 작성한다.
@Controller
@Log4j
public class LoginController {
@GetMapping("/customLogin")
public void loginInput() {
}
}
void 타입이므로 veiw 작성한다.