안녕하세요, 코린이의 코딩 학습기 채니 입니다.
개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.
security 커스텀 로그인 페이지
security-context.xml
<form-login
login-page="/member/memberLogin.do"
login-processing-url="/member/memberLogin.do"
default-target-url="/"
always-use-default-target="false"
username-parameter="memberId"
password-parameter="password" />
(http 태그는 생략함! http 태그 안에 작성해야합니다.)
login-page → 로그인 폼 요청 (작성필요)
login-processing-url → security 로그인 처리 (작성불필요)
username-parameter → 기본값 : username
password-parameter → 기본값 : password
default-target-url → 로그인 성공 후 리다이렉트 url (기본 값 root)
always-use-default-target → 항상 root로 리다이렉트? false : 이전 요청 페이지로 리다이렉트 처리
username-parameter, password-parameter는 로그인 폼에서 아이디/비밀번호를 작성하는 폼의 name값을 입력해줍니다.
memberLogin.jsp
<input
type="text" class="form-control" name="memberId"
placeholder="아이디" required>
<br />
<input
type="password" class="form-control" name="password"
placeholder="비밀번호" required>
로그인 폼의 아이디 인풋의 name이 memberId 이므로 지정해주었습니다.
(이 경우 password-parameter는 생략 가능. 기본 값이 password이므로!)
Controller
MemberSecurityController
@Controller
@RequestMapping("/member")
@Slf4j
public class MemberSecurityController {
@Autowired
private MemberService memberService;
@Autowired
private BCryptPasswordEncoder bcryptPasswordEncoder;
@GetMapping("/memberEnroll.do")
public String memberEnroll() {
return "member/memberEnroll";
}
@PostMapping("/memberEnroll.do")
public String memberEnroll(Member member, RedirectAttributes redirectAttr) {
try {
// 비밀번호 암호화
String rawPassword = member.getPassword();
String encodePassword = bcryptPasswordEncoder.encode(rawPassword);
member.setPassword(encodePassword);
log.debug("encodePassword = {}", encodePassword);
int result = memberService.insertMember(member);
redirectAttr.addFlashAttribute("msg", "회원가입이 정상적으로 처리 되었습니다.");
return "redirect:/";
} catch(Exception e) {
log.error("회원 가입 오류 : " + e.getMessage(), e);
throw e;
}
}
@GetMapping("/memberLogin.do")
public void memberLogin() {
}
}
로그인 폼을 연결하였으니, 더이상 security에서 제공하는 로그인 폼이 나오지 않는 것을 확인할 수 있습니다.
하지만 여기서 메모리 상에 저장해뒀던 honggd, 1234를 입력 후 로그인을 하면 403 에러가 발생합니다.
<authentication-manager> <!-- provider 관리 -->
<authentication-provider>
<user-service>
<user
name="honggd"
password="$2a$10$G7F3EfcXE8wJWUAsCznaoOqttim4MwJBNlWey5yuL5ZRMwRSdPvEi"
authorities="ROLE_USER"/> <!-- 메모리상에서 관리! -->
<user
name="admin"
password="$2a$10$G7F3EfcXE8wJWUAsCznaoOqttim4MwJBNlWey5yuL5ZRMwRSdPvEi"
authorities="ROLE_USER, ROLE_ADMIN"/>
</user-service>
<password-encoder ref="bcryptPasswordEncoder"/>
</authentication-provider> <!-- 인증관련 처리(인증주체) -->
</authentication-manager>
그 이유는 Post 요청으로 실행되는 로그인 → csrf 토큰 값이 없어서 발생한 것입니다.
CSRF (Cross-site Request Forgery) 공격 대비 설정
- 사용자의 특정 권한을 이용하여 공격자의 의도된 행동을 실행하게 만드는 공격법
- 특정 사용자의 권한수정, 등록/수정/삭제
- 모든 post 요청에 spring이 제공하는 token 값 검증
<csrf disabled="false"/>
(http 태그는 생략함! http 태그 안에 작성해야합니다.)
post 요청 시 csrf 토큰 값 제출
방법 ① - <form:form> 태그 이용
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<form:form
action="${pageContext.request.contextPath}/member/memberLogin.do"
method="post">
<div class="modal-body">
<input
type="text" class="form-control" name="memberId"
placeholder="아이디" required>
<br />
<input
type="password" class="form-control" name="password"
placeholder="비밀번호" required>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-outline-success">로그인</button>
<button type="button" class="btn btn-outline-success" data-dismiss="modal">취소</button>
</div>
</form:form>
폼 태그 내부에 input:hidden으로 csrf 토큰 값을 value로 가지고 있는 인풋 태그가 생성되었습니다.
해당 토큰 값이 일치하지 않다면, 공격자의 폼 제출로 간주하여 403 에러가 발생하게 되는 것이죠.
※ 만일 form taglib 오류가 난다면, 마찬가지로 해당 경로 하위에 jar파일을 lib 폴더 하위에 위치시키면 해결!
C:\Users\사용자\.m2\repository\org\springframework\spring-webmvc\5.2.22.RELEASE(본인 버전에 맞게 경로따라가기)
로그인 성공
security 커스텀 로그아웃 페이지
- security의 로그아웃은 반드시 POST 요청
security-context.xml
<logout
logout-url="/member/memberLogout.do"
logout-success-url="/" />
(http 태그는 생략함! http 태그 안에 작성해야합니다.)
post 요청 시 csrf 토큰 값 제출
방법 ② - input:hidden 이용
header.jsp
<form action="${pageContext.request.contextPath}/member/memberLogout.do" method="post">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit" >로그아웃</button>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
Controller
security가 처리해주므로 MemberSecurityController에 핸들러를 생성하지 않아도 정상적으로 잘 작동합니다.
로그아웃 성공
※ logout intercepr-url 설정
<intercept-url pattern="/member/memberLogout.do" access="permitAll"/>
기본 세션 관리하는 시간인 30분이 지난 다음 로그아웃을 누르면, 이미 세션이 해제된 상태에서 로그아웃 버튼을 누르게 됩니다.
그렇게 되면 logout 요청을 'isAnonymous' 상태에서 하게 되므로 오류가 발생하게 됩니다.
따라서 permitAll로 열어두어 오류 발생을 방지해줍니다.
error 처리
만일 아이디/비밀번호가 틀렸다면, url에 ?error가 붙게 되며 해당 로그인 페이지로 다시 리다이렉트 처리가 됩니다.
error가 발생하였다면, 그에 따른 사용자 알림이 있어야할 것이고, 이에 따른 스크립트 처리를 해주겠습니다.
memberLogin.jsp
<div class="modal-body">
<c:if test="${param.error != null}">
<div class="alert alert-danger alert-dismissible fade show" role="alert">
아이디 또는 비밀번호가 일치하지 않습니다.
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</c:if>
<input
type="text" class="form-control" name="memberId"
placeholder="아이디" required>
<br />
<input
type="password" class="form-control" name="password"
placeholder="비밀번호" required>
</div>
param.error가 존재한다면, 해당 alert가 띄워지도록 처리해보았습니다.
'Java > Spring' 카테고리의 다른 글
Spring) Security - DB에 있는 사용자 조회(로그인 처리), UserDetails/UserDetailsService (2) | 2022.09.11 |
---|---|
Spring) Security - 로그인 후처리 (0) | 2022.09.11 |
Spring) Spring Security - 설정 및 관련 설정 구체화 (설명 포함) (0) | 2022.09.10 |
Spring) Builder Pattern - 흐름 파악, @Builder (0) | 2022.09.10 |
Spring) Rest API - Delete (0) | 2022.09.07 |