🔐이번엔 로그인을 만들어보자!!
login.jsp의 form을 다음과 같이 수정하고 스크립트를 추가합니다. 이 스크립트는 login버튼이 눌리면 userVO에 정보를 담아 POST요청을 /에 보내는 스크립트입니다. 요청이 성공하고 반환받은 data의 success가 true라면 /board로 이동하고 아니라면 email 혹은 비밀번호를 확인해주세요라는 메시지를 띄웁니다.
<form class="user">
<div class="form-group">
<input type="email" class="form-control form-control-user" name="email" aria-describedby="emailHelp" placeholder="Enter Email Address...">
</div>
<div class="form-group">
<input type="password" class="form-control form-control-user" name="password" placeholder="Password">
</div>
<div class="form-group">
<div class="custom-control custom-checkbox small">
<input type="checkbox" class="custom-control-input" id="customCheck">
<label class="custom-control-label" for="customCheck">Remember Me</label>
</div>
</div>
<a id="login" class="btn btn-primary btn-user btn-block">
Login
</a>
<hr>
<a href="index.html" class="btn btn-google btn-user btn-block">
<i class="fab fa-google fa-fw"></i> Login with Google
</a>
<a href="index.html" class="btn btn-facebook btn-user btn-block">
<i class="fab fa-facebook-f fa-fw"></i> Login with Facebook
</a>
</form>
<script>
$("#login").click(function (){
var userVO = new Object()
userVO.email = $("[name='email']").val()
userVO.password = $("[name='password']").val()
$.ajax({
type:'POST',
url:'/',
data:JSON.stringify(userVO),
contentType:'application/json; charset=UTF-8',
success:function (data){
if(data.success)
location.href='/board'
else
alert('email 혹은 비밀번호를 확인해주세요!')
}
})
})
</script>
email로 유저의 정보를 가져올 수 있도록 수정합니다.
<mapper namespace="ac.kr.smu.mapper.UserMapper">
<insert id="save">
INSERT INTO user(name,email,password)
VALUES (#{name},#{email},#{password})
</insert>
<select id="checkEmailDuplication" resultType="int">
SELECT COUNT(*) FROM user where email=#{email}
</select>
<select id="findByEmail" resultType="UserVO">
SELECT * FROM user where email=#{email}
</select>
</mapper>
public interface UserMapper {
public void save(UserVO userVO);
public int checkEmailDuplication(String email);
public UserVO findByEmail(String email);
}
비밀번호를 확인해서 맞다면 true틀리면 false를 반환하도록 수정합니다.
public interface UserService {
public void save(UserVO userVO);
public boolean checkEmailDuplication(String email);
public boolean checkPassword(String email, String password);
}
@RequiredArgsConstructor
@Service
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
@Override
public void save(UserVO userVO) {
userMapper.save(userVO);
}
@Override
public boolean checkEmailDuplication(String email) {
return userMapper.checkEmailDuplication(email)==0;
}
@Override
public boolean checkPassword(String email, String password) {
return password.equals(userMapper.findByEmail(email).getPassword());
}
}
로그인 처리를 할 수 있도록 수정합니다. 비밀번호를 확인해서 맞다면 Map의 success에 true를 담아서 반환하고 아니라면 false를 담아서 반환합니다. 그리고 비밀번호가 맞다면 세션의 만료시간을 30분으로 설정하고 userSession이라는 이름으로 email을 저장합니다. 그리고 반환할 때 상태코드 200번을 반환합니다.
@RestController
@RequiredArgsConstructor
@RequestMapping("/")
public class LoginController {
private final UserService userService;
@GetMapping
public ModelAndView getLogin(){
return new ModelAndView("login");
}
@PostMapping
public ResponseEntity<?> postLogin(@RequestBody UserVO userVO, HttpSession session){
Map<String,Boolean> body = new HashMap<>();
boolean result = userService.checkPassword(userVO.getEmail(), userVO.getPassword());
body.put("success",result);
if(result) {
session.setMaxInactiveInterval(60*30);//초 단위로 세션의 만료시간 설정
session.setAttribute("userSession",userVO.getEmail());
}
return ResponseEntity.ok(body);
}
}
세션이란??
브라우저가 종료되기 전까지 클라이언트의 요청을 유지하는 기술
ResponseEntity는 객체와 HttpStatus 등등을 함께 반환할 수 있는 클래스입니다.
우리는 이제 로그인을 만들었으므로, 로그인이 되지 않은 사람들은 로그인, 회원가입 페이지 이외에는 접근을 하지 못하도록 해야합니다. 따라서 우리는 Interceptor로 이 처리를 할 것입니다.
LoginInterceptor 클래스를 추가합니다. 이 클래스는 request에서 세션을 받아와서, userSession이 없다면 로그인 페이지로 이동시키고 있다면 그대로 진행시키는 클래스입니다.
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
boolean chk=session.getAttribute("userSession") != null;
if(!chk)
response.sendRedirect("/");
return chk;
}
}
Interceptor란?
말 그대로 요청을 가로채는 클래스입니다. Controller로 요청이 가기 전에 혹은 요청이 끝나고 나서 조작을 가할 수 있는 AOP의 일종입니다.
다음 메소드를 Override해줍니다. interceptor를 추가하는 메소드입니다. 경로는 /board와 /post로 들어오는 모든 요청을 interceptor가 처리하도록 합니다.
@Override
public void addInterceptors(InterceptorRegistry registry) {
WebMvcConfigurer.super.addInterceptors(registry);
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/board").addPathPatterns("/post/**");
}
로그를 남겨서 접근을 했는지 확인해보겠습니다. LoginInterceptor를 다음과같이 수정합니다.
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
log.info(request.getRequestURI());
log.info("userSession is null? : "+(session.getAttribute("userSession")==null));
boolean chk=session.getAttribute("userSession") != null;
if(!chk)
response.sendRedirect("/");
return chk;
}
}
다음과 같이 로그인을 하지 않은 상태입니다.
로그인을 하지 않은 상태에서 접근을 하게 되면
interceptor가 요청을 가로채서 세션이 null인지 확인합니다. 이번 요청은 null이므로 다시 로그인 페이지로 이동시킵니다.