Spring Security 사용하여 로그인진행 했었다
지금은 Vue랑 Spring Security 연동 안됨
Vue랑 연동하는건 토큰까지 설정해줘야해서 방법이 좀 다르다
로그인 후
SecurityLoginService
에서
사용자로부터 받아온memberid
를 통해 DB에서 사용자의 정보 = member정보가 조회된다
// 사용자로부터 받아온 memberid 를 통해 DB에서 member정보 꺼내기
Member member = mRepository.findById(username).orElse(null);
조회된 정보는
User
타입에 담아 리턴해준다
// 조회된 정보에서 아이디, 암호, 권한정보를 가져온다
User user = new User(member.getUserid(), member.getUserpw(), role);
리턴되는 User
타입에는 아이디, 암호, 권한정보
가 담겨있다
로그인/로그아웃 성공시 홈화면으로 가게 설정되어있다
.defaultSuccessUrl("/")
➡️ 로그인 후 이동할 페이지 (권한상관없음)
.logoutSuccessUrl("/")
➡️ 로그아웃 후 이동할 페이지 (권한상관없음)
핸들러를 이용하여 사용자 로그인 후 권한별로 이동할 페이지를 지정해주기
- Spring Security의
AuthenticationSuccessHandler
를implements
구현상속 받아 사용
- SecurityLoginService에서 반환했던 User타입 꺼낸다
User user = (User) authentication.getPrincipal();
- 컬렉션 형태의 권한 꺼내기
⇒ 컬렉션 형태의 권한을 배열로 바꾼 후 배열에서 꺼내고 String으로 바꾸어 사용
Collection<Auth...> => Array[0] => String
String role = user.getAuthorities().toArray()[0].toString();
package com.example.handler;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
@Component
public class CustomesuccessLoginHandler implements AuthenticationSuccessHandler{
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// DetailService에서 리턴했던 타입으로 꺼내기
User user = (User) authentication.getPrincipal();
// 컬렉션타입의 권한 꺼내기
// Collection<Auth...> => Array[0] => String
// 컬렉션 형태의 권한을 배열로 바꾼 후 배열에서 꺼내고 String으로 바꾸어 사용
String role = user.getAuthorities().toArray()[0].toString();
if(role.equals("ADMIN")){ // 꺼낸 권한이 "ADMIN"인경우 이동할 위치 지정
response.sendRedirect(request.getContextPath() + "/admin/home.do");
} else if(role.equals("SELLER")){ // 꺼낸 권한이 "SELLER"인경우 이동할 위치 지정
response.sendRedirect(request.getContextPath() + "/seller/home.do");
} else if(role.equals("CUSTOMER")){ //꺼낸 권한이 "CUSTOMER"인경우 이동할 위치 지정
response.sendRedirect(request.getContextPath() + "/customer/home.do");
} else { //꺼낸 권한 데이터가 없는 경우 이동할 위치 지정
response.sendRedirect(request.getContextPath() + "/");
}
}
}
@RequiredArgsConstructor
어노테이션@RequiredArgsConstructor
어노테이션
@Getter
, @Setter
어노테이션 처럼 @RequiredArgsConstructor
어노테이션은
클래스에 선언된 final 변수들, 필드들을 매개변수로 하는 생성자를 자동으로 생성해주는 어노테이션이다
@RequiredArgsConstructor
는 초기화 되지않은 final 필드나,
@NonNull
이 붙은 필드에 대해 생성자를 생성해 준다
➡️ 새로운 필드를 추가할 때 다시 생성자를 만들어 관리해야하는 번거로움을 없애준다
= @Autowired
를 사용하지 않고 의존성 주입가능
= @RequiredArgsConstructor
어노테이션 사용시 원래 오토와이어드 자리에 final 붙여주면 된다
생성한 핸들러 사용하여 로그인 후 권한별 페이지 이동하기
@RequiredArgsConstructor
어노테이션` 사용, 원래 오토와이어드 자리에 final 붙여 사용한다
final SecurityLoginService securityLoginService;
final CustomesuccessHandler customesuccessHandler;
로그인시 핸들러 설정
.defaultSuccessUrl("/")
➡️ .successHandler(customesuccessLoginHandler)
로그아웃 성공시 핸들러 설정
.logoutSuccessUrl("/")
➡️ .logoutSuccessHandler(customesuccessLogoutHandler)
고객페이지/customer
에서 진행
package com.example.controller;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.dto.CustomUser;
@Controller
@RequestMapping(value = "/customer")
public class CustomerController {
@GetMapping(value = "/home.do")
public String getMethodName(Model model,@AuthenticationPrincipal CustomUser user){
if(user == null){ //user정보가 없는경우
return "redirect:/member/login.do"; //로그인페이지로 리턴
} // user정보가 있는경우
model.addAttribute("user", user); // user의 정보를 담아서
return "customer_home"; // /customer/home.do로 이동
}
}
로그아웃 버튼 만들어서 로그아웃도 진행
_csrf
토큰이 포함되어야 한다<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>홈화면</title>
</head>
<body>
<h3>고객홈화면</h3>
<hr />
세션에 들어있는 내용들 : <p th:text="${user}"></p>
아이디만 꺼내기 : <p th:text="${user.username}"></p>
권한만 꺼내기 : <p th:text="${user.authorities[0]}"></p>
<form th:action="@{/member/logout.do}" method="post">
<input type="submit" value="로그아웃">
</form>
<form>
</form>
</body>
</html>
// 403오류처리
// 접근불가 페이지일때 보여지는 url 주소
http.exceptionHandling().accessDeniedPage("/page403.do");
@GetMapping(value="/page403.do")
public String getMethodName() {
return "page403";
}
user에 더 많은 정보담기 위해 상속받아 추가로 포함할 내용을 넣어 사용
조회된 정보에서 아이디, 암호, 권한정보를 가져온다
User user = new User(member.getUserid(), member.getUserpw(), role);
User(=세션)에 추가로 포함할 내용을 넣기위해 클래스 상속 받아 추가할 내용 추가하기
package com.example.dto;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
// 세션에 포함할 내용을 추가하기 위한 클래스 상속
@Getter
@Setter
@ToString
public class CustomUser extends User {
// 변수 생성
String username;
String password;
Collection<GrantedAuthority> authorities;
String phone; // 추가할 정보
public CustomUser(String username, String password, Collection<GrantedAuthority> authorities, String phone) {
super(username, password, authorities);
this.username = username;
this.password = password;
this.authorities = authorities;
this.phone = phone;
}
}
User반환시 새로 상속받아 생성한 CustomUser
반환
➡️ User user ⇒ CustomUser user
// 로그인 화면에서 전달되어 호출되는 서비스
@Service
public class SecurityLoginService implements UserDetailsService{
@Autowired
MemberRepository mRepository;
// 로그인 화면에서 전달되어 호출되는 오버라이드 메서드
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("=====================username===================");
System.out.println(username);
// 위에서 memberid 를 통해 DB에서 member정보 꺼내기
Member member = mRepository.findById(username).orElse(null);
if(member != null){ // 조회된 회원정보가 있는 경우
// 권한정보는 그냥 만들면 안된다! 배열형태로 만들어줘야함
String[] str = { member.getRole() };
Collection<GrantedAuthority> role = AuthorityUtils.createAuthorityList(str);
// 조회된 정보에서 아이디, 암호, 권한정보를 가져온다
// User user = new User(member.getUserid(), member.getUserpw(), role);
// 상속받아 생성한 User로 반환
CustomUser user = new CustomUser(member.getUserid(), member.getUserpw(), role, member.getPhone());
return user;
}
else { // 조회된 회원정보가 없는경우
// 오류처럼 보이지 않게 하기 위해
String[] str = { "_" };
Collection<GrantedAuthority> role = AuthorityUtils.createAuthorityList(str);
// User user = new User("", "_", role );
// 상속받아 생성한 User로 반환
CustomUser user = new CustomUser("", "_", role, "");
return user;
}
}
}
받아오는 User값을 변경해준다
@AuthenticationPrincipal User user
➡️ @AuthenticationPrincipal CustomUser user
@GetMapping(value = "/home.do")
public String getMethodName(Model model,@AuthenticationPrincipal CustomUser user){
if(user == null){ //user정보가 없는경우
return "redirect:/member/login.do"; //로그인페이지로 리턴
} // user정보가 있는경우
model.addAttribute("user", user); // user의 정보를 담아서
return "customer_home"; // /customer/home.do로 이동
}
- script를 이용한 security logout 호출
- LogoutHandler를 이용한 security logout호출
script를 이용하여 로그아웃 처리를 하기 위한 로그아웃 버튼 생성
➡️ 화면이 로딩됨과 동시에 바로 로그아웃이 진행된다
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>로그아웃</title>
</head>
<body>
<form th:action="@{/member/logout.do}" method="post" id="form" style="display:none">
</form>
<script>
const form = document.getElementById('form');
form.submit();
</script>
</body>
</html>
script를 이용한 security logout 호출하기 ➡️
return "redirect:/member/logout.do"
컨트롤러에서 로그아웃 사용
@GetMapping(value = "logout.do")
public String logoutGET(){
return "member_logout";
}
LogoutHandler를 이용하여 로그아웃 처리를 하기 위한 로그아웃1 버튼 생성
<form th:action="@{/member/logout1.do}" method="get">
<input type="submit" value="로그아웃1">
</form>
스크립트 없이 핸들러로만 로그아웃
// 방법2. LogoutHandler를 이용한 로그아웃 처리
@GetMapping(value = "/logout1.do")
public String logout1GET(HttpServletRequest request,
HttpServletResponse response,
Authentication auth){
new SecurityContextLogoutHandler().logout(request, response, auth);
return "redirect:/";
}