배움 __IL/TIL 1기

TIL : 88번째- 230411 [4-2-화]

Mo_bi!e 2023. 4. 11. 18:53

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