I. 로그인구현
1. JSP / 서블릿 이용한 방법
(1) service 구현 및 컨트롤러
1) isvalidMember
@Service
public class DefaultMemberService implements MemberService{
@Autowired
private MemberRepository repository;
@Override
public boolean isvaildMember(String uid, String pwd) {
// TODO Auto-generated method stub
Member member = repository.findByUserName(uid);
if(member == null)
return false;
else if(!member.getPwd().equals(pwd)) //equls 주의!!
return false;
return true;
}
false 로만 한 이유는 로그인 실패 이유를 제시하는것은 계정보안상 이슈가 있기 때문이야
if는 그렇기 때문에 member 자체 여부만 확인하면 충분한거야
2)
@GetMapping("login")
public String login() {
return "/user/login";
}
@PostMapping("login")
public String login(
String uid,
String pwd,
HttpSession session) {
//위 아래중 뭐가 바람직? 아래가 바람직하
//컨트롤러는 입출력에 불과하기 때문에 아래가 바람직해
// Member member = menuservice.getByUserName(uid);
boolean isvalid = memberservice.isvaildMember(uid, pwd);
System.out.println(isvalid);
if(isvalid) {
session.setAttribute("username", uid);
if(returnURL != null)
return "redirect:" + returnURL;
return "redirect:/index";
}
return "redirect:login?error=X";
}
로그인페이지 받는 Get요청과 / 보내주는 post요청해주는거야!
3)
이 경우 post방식을 이용해서 해주자!
<form method="POST">
<div class="d-flex align-items-center">
<label class="d-none">아이디</label><input name="uid" class="btn btn-cancel" type="text" placeholder="로그인 아이디를 입력하세요">
</div>
<div class="d-flex align-items-center">
<label class="d-none">비밀번호</label><input name="pwd" class="btn btn-cancel" type="password" placeholder="비밀번호">
</div>
<div class="d-flex align-items-center justify-content-center">
<input type="checkbox">
<label>로그인 저장</label>
</div>
<div class="d-flex align-items-center justify-content-center">
<input class="btn btn-default" type="submit" value="로그인">
<a class="btn btn-cancel" href="">취소하기</a>
</div>
</form>
(2) 로그인 실패메시지 띄워주기
1)
div 로 기본세팅하기
<section class="login">
<h1 class="d-none">일반 로그인</h1>
<div class="" style="color:red;
font-weight: bold;
text-align: center;
margin-bottom:bold;">
</div>
<form method="POST">
<div class="d-flex align-items-center">
<label class="d-none">아이디</label><input name="uid" class="btn btn-cancel" type="text" placeholder="로그인 아이디를 입력하세요">
</div>
<div class="d-flex align-items-center">
<label class="d-none">비밀번호</label><input name="pwd" class="btn btn-cancel" type="password" placeholder="비밀번호">
</div>
<div class="d-flex align-items-center justify-content-center">
<input type="checkbox">
<label>로그인 저장</label>
</div>
<div class="d-flex align-items-center justify-content-center">
<input class="btn btn-default" type="submit" value="로그인">
<a class="btn btn-cancel" href="">취소하기</a>
</div>
</form>
</section>
style을 포함해서 넣어준다.
2)
로그인 실패할 때만 출력하게끔하기
@PostMapping("login")
public String login(String uid, String pwd) {
//위 아래중 뭐가 바람직? 아래가 바람직하
//컨트롤러는 입출력에 불과하기 때문에 아래가 바람직해
// Member member = menuservice.getByUserName(uid);
boolean isvalid = memberservice.isvaildMember(uid, pwd);
System.out.println(isvalid);
if(isvalid)
return "redirect:/index";
return "redirect:login?error";
}
?error 을 넣어주는거야
쿼리스트링방식으로 redirect 하기
3)
@PostMapping("login")
public String login(String uid, String pwd) {
//위 아래중 뭐가 바람직? 아래가 바람직하
//컨트롤러는 입출력에 불과하기 때문에 아래가 바람직해
// Member member = menuservice.getByUserName(uid);
boolean isvalid = memberservice.isvaildMember(uid, pwd);
System.out.println(isvalid);
if(isvalid)
return "redirect:/index";
return "redirect:login?error=X";
}
쿼리스트링에 X라는 값을 추가해준다
<login.html>
<section class="login">
<h1 class="d-none">일반 로그인</h1>
<div class="d-none"
th:class="${param.error} ? '' : 'd-none'"
style="color:red;
font-weight: bold;
text-align: center;
margin-bottom:bold;">
잘못
</div>
<form method="POST">
<div class="d-flex align-items-center">
<label class="d-none">아이디</label><input name="uid" class="btn btn-cancel" type="text" placeholder="로그인 아이디를 입력하세요">
</div>
<div class="d-flex align-items-center">
<label class="d-none">비밀번호</label><input name="pwd" class="btn btn-cancel" type="password" placeholder="비밀번호">
</div>
<div class="d-flex align-items-center justify-content-center">
<input type="checkbox">
<label>로그인 저장</label>
</div>
<div class="d-flex align-items-center justify-content-center">
<input class="btn btn-default" type="submit" value="로그인">
<a class="btn btn-cancel" href="">취소하기</a>
</div>
</form>
</section>
타임리프 조건문을 이용해서 해준다 param.error? 로 해준다.
이렇게 하면 상단에 '잘못'이라고 출력이 돼!
(3) 로그인 로그아웃
1)
session 을 받기
다만 세션에 정보가 많으면은 서버 과부하가 생겨, 이런경우에는 세션을 이용하지말자
만명 십만명이쓰면 부담이 되어버려...
2)
<ul class="main-menu d-none d-inline-flex-sm">
<li><a class="" href="/menu/list.html">카페메뉴</a></li>
<li><a class="" href="/notice/list.html">공지사항</a></li>
<li th:if="${session.username} == null"><a class="" href="/user/login.html">로그인</a></li>
<li th:if="${session.username} != null"><a class="" href="/user/login.html">로그아웃</a></li>
</ul>
세션에다가 username이 null인지 여부에 따라서 두가지가 다르게 보이게끔 하면 돼!
3)
로그인페이지 레이아웃쓰자
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="inc/layout">
<main layout:fragment="main">
<section>
<header class="header-default">
<h1 class="text-title1-h1">로그인}</h1>
</header>
<section class="login">
<h1 class="d-none">일반 로그인</h1>
<div class="d-none"
th:class="${param.error} ? '' : 'd-none'"
style="color:red;
font-weight: bold;
text-align: center;
margin-bottom:bold;">
잘못
</div>
<form method="POST">
<div class="d-flex align-items-center">
<label class="d-none">아이디</label><input name="uid" class="btn btn-cancel" type="text" placeholder="로그인 아이디를 입력하세요">
</div>
<div class="d-flex align-items-center">
<label class="d-none">비밀번호</label><input name="pwd" class="btn btn-cancel" type="password" placeholder="비밀번호">
</div>
<div class="d-flex align-items-center justify-content-center">
<input type="checkbox">
<label>로그인 저장</label>
</div>
<div class="d-flex align-items-center justify-content-center">
<input class="btn btn-default" type="submit" value="로그인">
<a class="btn btn-cancel" href="">취소하기</a>
</div>
</form>
</section>
<section class="register">
<h1 class="d-none">회원가입</h1>
<a href="signup.html">회원가입</a>
<a href="">아이디 찾기</a>
<a href="">비밀번호 찾기</a>
</section>
<section class="social-login">
<h1 class="d-none">소셜 로그인</h1>
<span>또는 다음으로 로그인</span>
<div>
<a class="icon icon-naver mx-2" href="">네이버 로그인</a>
<a class="icon icon-kakao mx-2" href="">카카오 로그인</a>
<a class="icon icon-youtube mx-2" href="http://localhost/login/oauth2/code/google">구글 로그인</a>
</div>
</section>
</section>
</main>
</html>
4)
모든 페이지 마다 해야해하는게 지금 번거롭기는 하지?
한편 로그인페이지 보내기전에 처음요청했던 페이지가 무엇인지 기억하고, 로그인 후에 다시 그 페이지로 가고싶으면
returnURL 을 하면 돼!
@GetMapping("/index")
public String index(HttpServletRequest request,
HttpSession session) {
//세션받기 학습용
//세션 생성하고(getSession) & 세션객체에 저장(setAttribute)
// request.getSession().setAttribute("test", "hehe");
// System.out.println((String) session.getAttribute("test"));
//세션이 없으면 로그인페이지로 보내기!
if(session.getAttribute("username") == null)
return "redirect:/user/login?returnURL=/admin/index";
return "admin/index";
}
로그인 후에 returnURL을 요청하면 돼!
@GetMapping("list")
public String list(@RequestParam(name="p", defaultValue = "1") int page,
@RequestParam(name="q", required = false) String query,
@RequestParam(name="c", required = false) Integer categoryId,
// @CookieValue("my") String myCookie
//@RequestHeader("Accept-Language") String language,
Model model,
HttpSession session
) throws UnsupportedEncodingException {
// 의미: 너 id pw 검증받은적있니? (마치 놀이공권 들어갈 때 팔찌 찬것처럼)
//세션 / 쿠키 이용해서 사용자가 인증되었던 적이 있는지를 확인함.
// if(너 로그인했니? => 아니오(==null) -> 로그인하고와)
if(session.getAttribute("username") == null)
return "redirect:/user/login?returnURL=/admin/menu/list";
// if(너 로그인했니? =>
// 네 => if(그런 너 어드민이니?)
// 아니오 -> 권한없다 얘~
List <MenuView> list = service.getViewList(page, categoryId, query);
model.addAttribute("list", list);
return "/admin/menu/list";
}
각각 return 을 넣어주고 로그인하고 와서 리턴될 url을 설정해줄거야!
불편하지? 필터를 이용해보자
(4) 서블릿 필터
1)
그런데 매번 모든 컨트롤러마다 반복하는게 번거로워 이런것을 대비하기위해서 필터를 이용할거야
클라이언트 서버 사이에 필터가있어! AOP 설명할 때 말했던 프록시 같은거야
서블릿 앞머리나 뒤에 빵또아처럼 하면은 작업을 한번만으로 줄일 수 있어
이거는 서블릿이 가지고 있는 영역이야
스프링에서는 서블릿(세션 등) 등을 쓰는건데, 필터는 사실 서블릿의 기능이야
2) 구현해보기
package kr.co.rland.web.config;
import java.io.IOException;
import org.springframework.stereotype.Component;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
// 루트 할 때!
@Component
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
System.out.println("필터가 실행되었습니다.");
}
}
이렇게 하고나서 어떤 페이지를 가도 콘솔에 필터가 실행되었다고 출력이 돼!
그런데 어디를 가도 다 백지야 또 에러도 안나!!!
왜냐하면 필터라서 그래!
필터는 수문장이야 입구에서 지키고 있어서그래
3)
filterchain을 이용하자
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
System.out.println("입구 필터가 실행되었습니다.");
chain.doFilter(request, response);
System.out.println("출구 필터가 실행되었습니다.");
}
doFilte()를 거쳐서 그래
필터를 잘 만져야해! 잘못만지면 css, image 는 전달이 안돼
특정한 것은 통과되면서 다른 것은 안되게끔 주의하자!!
4)
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest httpRequest = (HttpServletRequest) request;
//둘중에 하나를 선택하면 돼!
String uri = httpRequest.getRequestURI();
System.out.println(uri);
String url = httpRequest.getRequestURL().toString();
System.out.println(url);
System.out.println("입구 필터가 실행되었습니다.");
chain.doFilter(request, response);
System.out.println("출구 필터가 실행되었습니다.");
}
이 두가지 방식 중 하나를 선택 할 수 있어
uri / url 방식 두가지로 콘솔에다가 출력해보자
package kr.co.rland.web.config;
// 루트 할 때!
//@Component
public class AuthFilter implements Filter {
private static final String[] authUrls = {
"/admin/**", "/member/**"
};
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest httpRequest = (HttpServletRequest) request;
//둘중에 하나를 선택하면 돼!
String uri = httpRequest.getRequestURI();
System.out.println(uri);
String url = httpRequest.getRequestURL().toString();
System.out.println(url);
System.out.println("입구 필터가 실행되었습니다.");
chain.doFilter(request, response);
System.out.println("출구 필터가 실행되었습니다.");
}
}
전역으로 권한에 대한 url을 설정한다.
(5) 스프링 시큐리티 (Spring security)
1)
addstarter 에서 실행을 하는거야
그러면 자동으로 list에 가도 이런게 나오게 돼
좋은 라이브러리란 내가 원하는 방향으로 해줄 수 있게끔 해야해
비록 손안대고 코풀어서 좋긴하지만 내가 원하는대로 된거는 아니야
2)
id : user
pw : 상단 임시 pw
이걸로 입력해주면 돼!
3)
옛날에는 XML 방식, 자바 방식이 있었어
전자는 스프링3. 대 까지는 XML이야,
지금은 6.0 인데 그러다 보니 자바방식이 좋아
4)
package kr.co.rland.web.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import jakarta.servlet.http.HttpServletRequest;
@Configuration
public class RlandSecurityConfig {
@Bean //객체를 만들어서 리턴할건데, 설정해야하는 녀석이야
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//http 주소에 대한 필터에 대한 설정이야
//url에 따라서 통과여부를 가릴 수있어
http
.authorizeHttpRequests() //권한을 요청해
.requestMatchers("/admin/**") //요청에서 패턴으로 //* 하나 쓸 때는 그 페이지만 두개면 하부디렉토리 까지 다 포함한 재귀경로가 되는거야
.hasAnyRole("ADMIN")
.requestMatchers("/member/**").hasAnyRole("ADMIN", "MEMBER")
.anyRequest().permitAll();
return http.build();
//언제부터 빌드 시스템이 들어왔어
//마치 문자열빌드처럼 설정들 한번에 다 하기
}
}
IOC컨테이넝 담기위해서 @bean을 쓴다
그리고 서블릿방식을 응용한 filterChain 을 써준다
필터체인에서 받은 http 를 이용해서 나머지 이용하는거야
권한을 요청하고 -> 매핑되는 url 작성 -> 어떤 권한을 가진역할들인지를 순서대로 지정해주면 돼!
그리고 return 은 http 가 아니라 build()를 하는거야
빌드시스템을 이용하는거야
1. 보충
2. 회고
1) 서블릿방식으로 하기에는 다양한 경우의 수를 대비하기위한 조건문을 매번 만드는것이 번거롭다
이런 점을 해결하기위해서 spring security 가 만들어져서 좋은것 같다.
'배움 __IL > TIL 1기' 카테고리의 다른 글
TIL : 92번째- 230418 [4-3-화] (0) | 2023.04.18 |
---|---|
TIL : 91번째- 230417 [4-3-월] (0) | 2023.04.17 |
TIL : 87번째- 230410 [4-2-월] (0) | 2023.04.10 |
TIL : 86번째- 230407 [4-1-금] (0) | 2023.04.07 |
TIL : 83번째- 230404 [4-1-화] (0) | 2023.04.04 |