로그인

Daniel_Yang·2022년 3월 30일
0

로그인(+관리자 로그인) 고려사항


  • 아이디와 비밀번호 로그인
  • 로그인 세션 유지
  • 아이디 기억
  • 로그인 유효성 검사
  • 로그아웃
  • 로그인을 하고 다시 원래 URL로 이동
  • 다국어처리
  • 아이디 및 비밀번호 찾기
  • 스프링 시큐리티 적용
  • 부가 기능: 로고를 클릭하면 메인 이동, 계정이 없다면 회원가입 이동

핵심코드


아이디와 비밀번호 로그인

  • Mapper
	<select id="login"  parameterType="user" resultType="user" >
		SELECT * FROM user_info
		WHERE UI_ID = #{uiId}
		AND UI_PW = #{uiPw}
		
	</select>
  • view

    • 로그인 성공, 실패 모두 로그인 화면으로 들어온다. 메시지에 따라 이동

      $(document).ready(function(){
        var loginMsg="${loginMsg}";
        if(loginMsg!=""){
          alert(loginMsg);
          if(loginMsg=="로그인 성공!"){ 
            // 로그인 성공을 보여주고나서 이동
            location.href="${path}/prjList.do"; 
      
          }
        }
      
      });

로그인 세션 유지 링크

  • 두가지 방법이 있다. Session 객체냐 @SessionAttributes 냐
    • @SessionAttributes: 스프링은 세션을 더 편리하게 사용할 수 있도록 @SessionAttribute을 지원합니다.
      세션을 찾고, 세션에 들어있는 데이터를 찾는 번거로운 과정을
      스프링이 한 번에 편리하게 처리해주는 것을 알 수 있습니다.
      참고로 이 기능은 세션을 생성하지 않습니다.
    • @SessionAttribute를 이용해 세션의 값을 객체로 바인딩 할 수 있습니다.
      알맞은 key값을 지정하여 객체에 값을 바인딩합니다.
      required 속성을 이용해 필수 여부를 지정할 수도 있습니다.
  • Controller
    메서드 매개변수: HttpSession session, 유저 객체
    
    유저 인스턴스 = service.login(유저 객체)
    session.setAttribute("유저", 유저 인스턴스)
    Controller 어노테이션: @SessionAttributes("유저객체")
    // vo객체 이름이랑 앞글자가 소문자인거 말곤 같아야한다
    
    Controller 내부
    	@ModelAttribute("user_info")
    	public USER_INFO getUser() {
    		return new USER_INFO();
    	}
    
    메서드 매개변수 : @ModelAttribute("유저객체") 유저객체 sch, Model d
    
    d.addAttribute("user_info",sch);

아이디 기억

  • checkbox이기에 boolean형태로 controller에 넘어간다.

  • Controller

    • 느낀점: 음... controller 이렇게 지저분하게 코드를 짜도 괜찮을까..? Service 쪽에 넘길 걸...

      				// 아이디 기억
               if(saveId) {
                    // 쿠키 생성해서 아이디속성에 저장 
                        // => 사용하려면 EL태그 cookie.id.value로 사용하라
                    Cookie cookie = new Cookie("id", sch.getUiId()); 
      
                    //	응답에 저장해서 보내
                    response.addCookie(cookie);
                } else {
      
                    Cookie cookie = new Cookie("id", sch.getUiId()); 
      
                    // 쿠키를 삭제
                    cookie.setMaxAge(0); 
      
                    response.addCookie(cookie);
                }
      			```
      
  • view

            아이디: <input value="${cookie.id.value}> 
            아이디기억 창: <input type="checkbox" name="saveId" id="checkbox-signin">

로그인 유효성 검사

  • view
			function loginchk(){
				if($("[name=uiId]").val().trim()==""){
					alert("아이디를 입력해주세요.");
					// 값이 들어가있지 않으면 false로 리턴 
					$("[name=uiId]").focus();
					return false;
				}else if ($("[name=uiPw]").val().trim()==""){
					alert("비밀번호를 입력해주세요.");
					$("[name=uiPw]").focus();
					return false;
				}else{
					return true;
					// 유효성 검증 후 값이 전부 들어가 있으면 main으로 이동 
				}
			}

로그아웃

  • Session 객체 냐 @SessionAttributes 냐에 따라 다르다.
    • 허면 @SessionAttributes의 자동로그아웃은 어떻게 처리???
      메서드 매개변수: HttpSession session
      session.invalidate(); // 세션 종료

      메서드 매개변수: SessionStatus sessionStatus
      sessionStatus.setComplete(); // 
  • @SessionAttribute를 사용하게되면
    • 아마 setComplete()로 충분할 수 있지만 혹시 모르니 session 종료하는게 나을 수도?

      // 세션 종료
      		session.invalidate(); 
      		// @SessionAttritbute를 사용하게되면 session에도 값을 담게 된다. 
      		// 그래서 session.getAttribute가능
      			// but session.invalidate()를 통해 세션 종료해도 계속 값이 남는다. 
      			// 아마 스프링 컨테이너에 계속 있는듯? 
      			// 세션 만료 후에는 세션에서 가져올 때(getAttributes)는 안되지만,
      			// 이렇게 @ModelAttribute로 가져오는건 그대로 유지가 되네
      	
      		
      		s.setComplete();
      
  • 자동로그아웃
    	<session-config> 
    <!-- jsp는 보통 was 서버인 tomcat으로 실행되는 경우가 많기에 
    		tomcat의 session time 받아서 -->
    		<session-timeout>30</session-timeout> 
    	</session-config>

로그인을 하고 다시 원래 url로 이동

  • 원래 URL Javascript (url은 컨트롤러 맵핑 주소 해당)
    // 로그인할 때만 들어올 수 있도록 // 참조: https://erim1005.tistory.com/28
    			
    			<c:if test="${empty user_info.uiName}">
    			
    				let url = '${requestScope['javax.servlet.forward.servlet_path']}'; // 컨트롤러 url주소
    				let queryString = '${requestScope['javax.servlet.forward.query_string']}'; //queryString 
    				url += (queryString != '')? '?'+queryString: '';
    		
    				// JSP 현재 url 정보 얻기 => ${pageContext.request.requestURL}는 실제 jsp 물리적 경로...
    				
    				alert("로그인 후, 이용해주세요."); 
    				
    				location.href="${path}/loginFrm.do?toURL="+url;
    			</c:if>
  • view
    <input name="toURL" value="${toURL}" hidden />
  • Controller
    if(toURL.equals("")) { 
    	// main에서 로그인할 때는, header 쪽에서 주는 toURL이 없으니
    				return "main_login//login.jsp";
    			}
    			
    			return "redirect:"+toURL;

다국어 처리

  • 프로세스

    • 해당 선택 언어에 따른 등록된 unicode로 된 메시지 변경
    • 다국어 패키지 → properties 파일(영어, 한국어)
      → 여기서 한글을 입력하면 유니코드가 알아서 생성된다. (커서 올리면 원래 글자가 보인다)
      → 디스패처서블릿에 다국어처리를 위한 모듈 등록
    • view 파일을 만들고 → lib 설정 → spring:message 설정 → 언어 선택 설정 (code 속성값으로 해당 언어를 선택한다)
      → 언어에 따라 변경되어야할 값들은 <spring:message code='pwd'/> 과 같은 형식으로 설정한다.
    • JS에서 언어에 따른 선택에 따라 Controller 호출되게끔
      → Controller에서 언어를 요청값으로 받아서 Locale 클래스의 매개변수 생성자로 생성 후,
      이를 localeResolver.setLocale(request, response, locale); 해주면 된다.
      - 변환기 링크
  • dispatcherservlet

    • localeResolver는 4가지가 있는 데, 그 중 SessionLocaleResolver 사용

      <bean id="messageSource"
      		class="org.springframework.context.support.ResourceBundleMessageSource">
      		<property name="basenames">
      			<list>
      				<value>message.msg</value>
      			</list>
      		</property>	
      	</bean>
      	<bean id="localeResolver" 
      	class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>
  • controller
    • locale이라는 클래스 객체를 통해 언어 매개변수(lang)로 생성자 만든다.

    • 스프링 MVC는 LocaleResolver를 이용해서 웹 요청과 관련된 Locale을 추출하고,
      Locale 객체를 이용해서 알맞은 언어의 메시지를 선택

    • localeResolver는 언어를 선택하는 방법이 여러가지 있으나 여기서는 session을 활용하였다.

      package springweb.a02_mvc.a01_controller;
      
      import java.util.Locale;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RequestParam;
      import org.springframework.web.servlet.LocaleResolver;
      
      @Controller
      public class MultiLangCtrl {
      	// 컨테이너에 선언된 언어선택 객체..
      	@Autowired(required=false)
      	private LocaleResolver localeResolver;
      	
      	// 1. 초기화면 호출.
      	@GetMapping("multi.do")
      	public String multi() {
      		return "WEB-INF\\multiLanguage.jsp";
      	}
      	// 2. 언어 선택에 따른 변환처리.
      	@GetMapping("choiceLan.do")
      	public String choide(@RequestParam("lang") String lang,
      			HttpServletRequest request,
      			HttpServletResponse response) {
      		System.out.println("선택한 언어:"+lang);
      		Locale locale = new Locale(lang);
      		localeResolver.setLocale(request, response, locale);
      		
      		return "WEB-INF\\multiLanguage.jsp";
      	}
      	
      }
  • view
    - spring tag lib 호출 선언
    	<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>	
    
    $(document).ready(function(){
    		<%-- 
    		
    		# select 옵션에 의해서 
    		선택되었을 때, 언어의 변경 처리하는 controller 호출 및 처리된 결과 확인..
    		
    		만약 값이 바뀌었을 떄 빈 값이 아니라면 해당 언어로 변경
    		--%>
    		$("#selectLan").val("${param.lang}")
    		$("#selectLan").change(function(){
    			if($(this).val()!=""){
    				location.href="${path}/choiceLan.do?lang="+$(this).val();
    			}
    		});
    	
    	});
    
    html 텍스트는 글자 대신 <spring:message code="id"/> 같이

아이디 및 비밀번호 찾기

  • 아이디 찾기
    • 이름과 이메일 주소를 가지고 아이디 찾기 진행

    • AJAX를 활용하여 컨트롤러에서 해당하는 아이디가 있는지 여부에 따라

      // 확인용
      
      function findId(){
            $.ajax({
            url : "${path}/getUserId.do",
            type : "get",
            dataType : "json",
            data : $("form").serialize(),
            success : function(data){
            	userId = data.userId;
                if(userId != null){
                    alert("귀하의 아이디는 "+userId+"입니다.");
          
                }else{
                    alert("정보가 올바르지 않습니다.");
                }
            }
      })
      			}
  • 비밀번호 찾기
    • 이름과 아이디를 통해 비밀번호 찾기 진행

    • 비밀번호 찾기가 진행되면 해당하는 아이디의 비밀번호를 임시비밀번호로 변경

    • 해당하는 정보가 있다면 가입했을 때 작성한 이메일 주소로 임시비밀번호 전송

    • 프로필 수정해서 비밀번호를 바꾸도록 하는 방식

      // 확인용
      
      function findPw(){
      	
              $.ajax({
              url : "${path}/getUserPw.do",
              type : "get",
              dataType : "json",
              data : $("form").serialize(),
              success : function(data){
              		userPw = data.userPw;
                    if(userPw != null){
                        alert("임시 비밀번호를 이메일로 보냈습니다.");
              
                    }else{
                        alert("정보가 올바르지 않습니다.");
                    }
                }
      })
      }
    • Controller

      @RequestMapping("/getUserPw.do")
      	public String getUserPw(USER_INFO user, Mail email, Model d) {
      		
      		String pw = service.getPw(user);
      		
      		if(pw==null) {
      			
      			return "pageJsonReport";
      		}
      		// 랜덤 비밀번호로 변경
      		service.updateTempPw(email, user);
      
      		// 메시지용
      		d.addAttribute("userPw", 1);
      	
      		return "pageJsonReport";
      	}
    • Service

      // 메일 발송 메서드 
      public void sendMail(Mail email, String receiever, String tempPw) {
      		
      		// 멀티미디어형 메일 데이터 전송.
      		MimeMessage mmsg = sender.createMimeMessage();
      	
      		try {
      			// 제목 설정
      			email.setTitle("BORAM3 PMS 임시비밀번호입니다.");
      			mmsg.setSubject(email.getTitle());
      			
      			// 내용 설정
      			String content = "임시비밀번호를 전송합니다. 로그인 후, "
      					+ "비밀번호 수정을 진행해주세요. <br>"
      					+ "임시비밀번호: <span style='font-weight: 700'>"
      					+tempPw +"</span>"
      					+ "<br><br> BORAM3 PMS로 "
      					+ "<a href='http://14.33.134.85:7090/borampms/main.do'>이동</a>";
      			
      			email.setContent(content);
      			// html 태그를 넣어주기 위해
      			mmsg.setContent(email.getContent(), "text/html;charset=euc-kr");
      						
      			// 수신자 설정
      			email.setReciever(receiever);
      			mmsg.setRecipient(RecipientType.TO, new InternetAddress(email.getReciever()));
      			
      			// 발송 처리.
      			sender.send(mmsg);
      			
      		} catch (MessagingException e) {
      			System.out.println("메일 발송 에러:"+e.getMessage());
      		} catch (Exception e) {
      			System.out.println("일반 에러 발생:"+e.getMessage());
      		}
      	
      	}
      // 임시비밀번호로 변경 후, 메일 발송 시작
      public void updateTempPw(Mail email, USER_INFO user) {
      		
      		String tempPw = "";
      		int idx = 0;
      		char[] charList = new char[] {
      				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
      				'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
      
      		for (int i = 0; i < 8; i++) {
      			idx = (int)(Math.random() * 26);
      			tempPw += charList[idx];
      		}
      		
      		// 새 비밀번호로 설정
      		user.setUiPw(tempPw);
      		
      		// 이메일 주소 가져오기
      		String receiver = dao.getEmail(user);
      		
      		// 메일 발송
      		sendMail(email, receiver, tempPw);
      
      		dao.updateTempPw(user);
      	};

스프링 시큐리티 적용

  • Spring-application.xml 에서 db 자원 작성
  • Spring-security.xml 에서 로그인 정보, 인가 정보 ex) admin, manager, user 등
  • dispathcerServlet.xml 에서 스프링 시큐리티 controller
  • web.xml 에서 contextconfigLocation으로 spring-app/security.xml 두 파일을 등록, 스프링시큐리티필터체인
  • 권한 계정: DB 로 처리하거나, spring-security.xml에 처리
    • 계정의 비밀번호 앞에는 {noop}이 기본 세팅된다.

      <sec:authentication-manager>
      	
      		<!-- 
      		문자열로 권한 접근.. -->
      		<sec:authentication-provider>
      			<sec:user-service>
      				<sec:user name="himan" password="7777" 
      				          authorities="ROLE_USER" />
      				<sec:user name="manager" password="qwer" 
      				          authorities="ROLE_MANAGER" />
      				<sec:user name="admin" password="asdf" 
      				          authorities="ROLE_ADMIN,ROLE_USER" />
      			</sec:user-service>
      		</sec:authentication-provider>
      		
      		<!--DB로 처리시 --> 
      		<sec:authentication-provider ref="customAuthenticationProvider" />
      
      		<sec:authentication-provider
      			user-service-ref="customUserDetailsService" />
      
      		<sec:authentication-provider>
      			<sec:jdbc-user-service data-source-ref="dataSource"
      				id="jdbcUserService" />
      			
      			<sec:password-encoder ref="passwordEncoder" />
      			
      		</sec:authentication-provider>		
      	</sec:authentication-manager>
  • 디스패처서블릿 ⇒ admin 인 경우, /admin 하위 경로들은 다 해당이 된다.
<mvc:view-controller path="/index" view-name="/view/index.jsp" />
	
	<mvc:view-controller path="/admin/" view-name="/manager/managerUser.jsp" />	
	<mvc:view-controller path="/adminLoginform" view-name="/main_login/adminLogin.jsp" />
	<mvc:view-controller path="/security/accessDenied" view-name="/view/security/accessDenied.jsp" />

0개의 댓글