예제 순서

 

- spring_board_security

- spring_board_security_using_db

 

 

 

오늘 공부 한 것

 

- 화면에 출력되는 것을 몇개만 잘라서 보여줘야됨 페이징필요 예제) spring_board

- 쿼리문 

  ㅇ 시작번호와 끝번호를 쿼리문에 전달 필요

    ㅁ StartEnd dto객체를 통해 시작값과 종료값을 담아 전달

 

 

package kr.co.jhta.board.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

// lombok을 통해 dto 설정
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StartEnd {
	private int startNo;
	private int endNo;	
}

    ㅁ sqlMapConfig의 별칭을 지정해서 쿼리문을 전달 할 것임

<typeAlias type="kr.co.jhta.board.dto.StartEnd" alias="se"/>

    ㅁ dao에서 인자값으로 시작번호와 종료번호를 전달하고

       객체로 감싸서 mapper로 전달

@Override
	public List<BoardDTO> selectAll(int startNo, int endNo) {
		
		StartEnd se = new StartEnd(startNo, endNo);
		
		logger.info("ss :  "+ ss);

		return ss.selectList("kr.co.jhta.board.selectAll", se);
	}

    ㅁ service에서도 인터페이스와 구현클래스에 인자값을 전달

@Override
	public List<BoardDTO> readAll(int startNo, int endNo) {
		logger.info("dao : "+ dao);

		return dao.selectAll(startNo, endNo);
	}

    ㅁ 컨트롤러에서 서비스로 인자값을 전달

model.addAttribute("list", bs.readAll(startNo, endNo));

    ㅁ BoardMapper에서 쿼리문을 수정

<select id="selectAll" resultType="bdto" parameterType="se">
		SELECT RN , BNO, WRITER, TITLE, CONTENTS, REGDATE, HITS, IP, STATUS
		FROM ( SELECT ROWNUM RN , BNO, WRITER, TITLE, CONTENTS, REGDATE, HITS, IP, STATUS
				FROM (SELECT BNO, WRITER, TITLE, CONTENTS, TO_CHAR(REGDATE,'YYYY/MM/DD') REGDATE, HITS, IP, STATUS
					FROM BOARD
					ORDER BY BNO DESC )
				WHERE ROWNUM &lt;=#{endNo} )
		WHERE RN &gt;=#{startNo}
	</select>

- 전체 게시물수 출력

  ㅇ 컨트롤러 => 서비스 => dao => mapper (전체 카운트)를 통해 게시물의 수 전달

// Controller 총 게시물 수를 받아 변수에 대입
int totalNumber = bs.getTotal();

// BoardService 인터페이스 추상메서드 선언
public int getTotal();

// BoardServiceImple 오버라이드
@Override
	public int getTotal() {
		
		return dao.getTotal();
	}

// Dao 인터페이스 추상메서드 선언
public int getTotal();

// BoardOracleDAO 오버라이드
@Override
	public int getTotal() {
		// 1건만 가져오면 됨
		return ss.selectOne("kr.co.jhta.board.totalCount");
	}

// BoardMapper 별칭 지정 및 쿼리문 작성
<select id="totalCount" resultType="int">
		SELECT count(bno) cnt
		FROM board
</select>

- 현재 페이지 번호, 현재 페이지 게시물 시작번호, 현재 페이지 게시물 끝번호,

  페이지당 게시물 수, 총 페이지수 컨트롤러에서 구한 후 list페이지로 넘겨서

  출력되는 것을 확인

// 현재 페이지 번호
// int currentPageNo = 1;
// 총 게시물 수
int totalNumber = bs.getTotal();
// 시작번호		
int startNo = (currentPageNo-1)*10+1;
// 끝 번호
int endNo = currentPageNo*10;
// 페이지당 게시물 수
int countPerPage = 10;
// 총 페이지 수
int totalPage = (totalNumber%countPerPage==0)? totalNumber/countPerPage:
totalNumber/countPerPage+1;
// 페이지 시작 번호
int startPageNo = currentPageNo-5<=0? 1:currentPageNo-5;
// 페이지 끝 번호
int endPageNo = startPageNo+10 >= totalPage? totalPage:startPageNo+9;

- 리스트 페이지 하단 부에 반복문을 통해 전달받은 시작페이지번호와

  종료 페이지번호 만큼 반복 반복된 수만큼 선언한 i변수의 값을 증가 되고

  화면에 출력 => a태그를 통해 list url과 쿼리스트링으로

<c:forEach var="i" begin="${startPageNo}" end="${endPageNo}">
	<a href="list?currentPageNo=${i}"><button>${i}</button></a>
</c:forEach>

 

@RequestMapping(value = "/list")
public String list(Model model,
	@RequestParam(name = "currentPageNo", defaultValue = "1")int currentPageNo)
// 전달받은 파라미터에 초기값을 지정하면 굳이 변수를 선언 할 필요가 없다.

- 이전 버튼과 다음 버튼을 구현

boolean prev = currentPageNo > 5? true : false;
<c:choose>
	<c:when test="${prev}">
		<a href="list?currentPageNo=${currentPageNo-5}">
			<button>[이전]</button>
		</a>
	</c:when>		
</c:choose>	
boolean next = currentPageNo+5 >= totalPage? false : true;
<c:choose>
	<c:when test="${next}">
		<a href="list?currentPageNo=${currentPageNo+5}">
			<button>[다음]</button>
		</a>
	</c:when>		
</c:choose>

 

- spring security 보안 강화 예제) spring_board_security

  ㅇ 인증(Authentication) : 본인이 무엇인가를 증명하는 행위

  ㅇ 인가(Authorization) : 권한 부여 => 인증을하면 나의 등급에 맞게 부여되는 권한

 

  ㅇ security_context.xml sprig confing 생성 -> 보안관련 설정 여기다 할 것임

    ㅁ web.xml 추가
DelegatingFilterProxy : 모든 요청은 이 프록시필터를 거친다. 스프링시큐리티는 이를 통해 인증, 인가를 수행

<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
		/WEB-INF/spring/root-context.xml
		/WEB-INF/spring/appServlet/security-context.xml
		</param-value>
	</context-param>

  ㅇ pom.xml(web, taglibs, core, config) 추가 : mvn repository 사용

		<!-- security core -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>5.2.2.RELEASE</version>
		</dependency>
		<!-- security web -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>5.2.2.RELEASE</version>
		</dependency>
		<!-- security config -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>5.2.2.RELEASE</version>
		</dependency>
		<!-- security taglibs  -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-taglibs</artifactId>
			<version>5.2.2.RELEASE</version>
		</dependency>

  ㅇ security-context.xml namespace security 설정

 

 

  ㅇ security-context.xml security 설정추가

	<!-- * 현 디렉토리의 모든 파일 ** 하위 디렉토리까지 모두  -->
	<!-- 
         ROLE_ 형식은 반드시 지켜야됨  
    	 pattenr에 해당하는 요청을 받았을 때, access로 설정된 권한을
         가지고 있어야 접근 가능(지정된 권한만 허용)
    -->
	<!-- ex) url로 board가 왔을 때 인증화면을 띄운다.  -->
	<security:http auto-config="true">
		<security:intercept-url pattern="/board/*" access="isAuthenticated()"/>
		<security:intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN')"/>
		<security:intercept-url pattern="/public/**" access="permitAll"/>
		<security:intercept-url pattern="/member/**" access="hasRole('ROLE_USER')"/>
	</security:http>
	
    <!-- 
	<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
				<security:user name="user" password="123" authorities="ROLE_USER"/>
				<security:user name="admin" password="123" authorities="ROLE_ADMIN,ROLE_USER"/>
			</security:user-service>
		</security:authentication-provider>		
	</security:authentication-manager>
	<!-- 요청이 들어 올 때 이걸 잡아내는 필터가 필요함  -->

  ㅇ web.xml 필터추가
    ㅁ DelegatingFilterProxy : 모든 요청은 이 프록시필터를 거친다. 스프링시큐리티는 이를 통해 인증, 인가를 수행

<!-- 연결이 요청이오면 어느 필터 접근하는지 파악하고 시큐리티가 동작하게 됨  -->
	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

  ㅇ Controller에서 security에서 지정한 요청으로 전체 url 수정

@RequestMapping(value = "/board/list")
	public String list(Model model,

  ㅇ home페이지에서 a태그 요청URL을 board/list로 수정

<a href="board/list">게시판 가기</a>

  ㅇ home 페이지에서 a태그를 통해 board/list url로 접근시 인증페이지 출력 확인

 

 

ㅇ security 설정에서 지정한 아이디와 비밀번호 입력 후 진행시 오류 발생 확인

  ㅁ why?

      spring 4.0까지는 아무 비밀번호나 텍스트롤 사용해도 되었는데 5.0부터는 보안 문제로

      password를 암호화해서 체크 필요

  ㅁ 임시 해결 :  한시적으로 password에 noop을 지정해서 진행가능

                      인증에 대한 처리를 쉽게 spring을 통해 구현 가능

<security:user name="user" password="{noop}123" authorities="ROLE_USER"/>
<security:user name="admin" password="{noop}123" authorities="ROLE_ADMIN,ROLE_USER"/>

  ㅇ 로그인을 한 번하면 재검사를 하지 않음.

    ㅁ JSESSIONID : 서버에서 인증가능한 키 wat tomcat was 마다 이름이 조금씩 다름

    ㅁ session이 끊기지 않는한 로그인 상태 유지 됨

 

 

    ㅁ 브라우저 종료하고 다시 키면 JSESSIONID가 다름. 로그인이 다시 필요함

  ㅇ 전체 메서드에 공통으로 들어갈 url을 requestMapping을 통하여 추가

@RequestMapping("/board")

 

- write 기능 구현

  ㅇ jstl talib 추가

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

  ㅇ 인증받은 유저 아이디값으로 작성자 고정 및 변경 못 하게 지정

<input type="text" name="" id="" value="<sec:authentication property="principal.username" />" 
readonly="readonly"/>

  ㅇ 서버가 발급한 특정한값을 입력할때만 수정을 받아줄게.

      이거 내 사이트에서 발급한 토근이 맞아? 일련의 수를 실어줌??????

  ㅇ 토근 발급

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token }"

토큰값 발급 확인

 

  ㅇ security-context.xml 사용하지 않게 설정 할 수도 있음.

<security:csrf disabled="true"/>

  ㅇ Controller에서 redirect 사용시에 url board추가해줘야 진행됨

return "redirect:/board/list";

  ㅇ post로 url전달 시에 토큰 키를 발급해줘야 됨.

 

- 커스텀 로그인 페이지

- spring은 bootstrap 기반으로 이루어져있음(4.0)

  ㅇ 사용자 정의용으로 커스텀하게 로그인페이지를 구성 가능

  ㅇ security-context.xml 커스텀 로그인 추가

  ㅇ security:http 안에 

<security:form-login login-page="/common/cLogin"/>

  ㅇ

 

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>사용자 정의형 로그인 페이지</title>
</head>
<body>
	<h1>사용자 정의형 로그인 페이지</h1>

	<h2>
		<c:out value="${error}" />
	</h2>
	<h2>
		<c:out value="${logout}"/>
	</h2>
	<c:url value="/login" />
	<form action="<c:url value="/login" />" method="post">
    <table>
		<tr>
			<th>ID</th>
			<td>
				<input type="text" name="username" id="" value="user" />
			</td>
		<tr>
			<th>password</th>
			<td>
				<input type="text" name="password" id="" value="123"/>
			</td>
		</tr>
		<tr>
			<td colspan="2">
				<input type="submit" value="로그인" />
				<input type="hidden" name="${_csrf.parameterName}" 
				value="${_csrf.token }"/>
			</td>	
		</tr>
	</table>
	</form>
</body>
</html>

 

- CommonController 클래스 생성

- Get방식으로 url요청이 올 경우 진행

package kr.co.jhta.board.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class CommonController {
	private static final Logger logger = LoggerFactory.getLogger(CommonController.class);
	
	@RequestMapping(value = "/common/cLogin")
	public void login(String error, String logout, Model model) {
		
		if(error!= null)
			model.addAttribute("error", "로그인 중 에러 발생");
		else if (logout!=null)
			model.addAttribute("logout", "logout");
	}

 

- 로그아웃 기능 구현

  ㅇ list 페이지 a태그를 사용하여 

	<sec:authorize access="isAuthenticated()">
		<!-- c:url을 쓰면 contextpath를 포함하여 url값을 받는다.  -->
		<a href="<c:url value="/common/customLogout" />">로그아웃</a>
	</sec:authorize>	

  ㅇ security-context.xml 설정 추가 security:http 안에

			<security:logout delete-cookies="JSESSIONID"
			invalidate-session="true" 
			logout-url="/common/customLogout" logout-success-url="/" />
            

  ㅇ Controller 추가

	@RequestMapping(value = "/common/customLogout")
	public void logoutGet() {
		logger.info("커스텀 로그아웃");
	}

  ㅇ

 

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>logout</h1>
	<c:url value="/common/customLogout"/>
	<form action="<c:url value="/common/customLogout"/>" method="post">
	<input type="hidden" name="${_csrf.parameterName}" 
	value="${_csrf.token }"/>
	<input type="submit" value="로그아웃" />
	</form>
</body>
</html>

 

- 아이디와 패스워드를 굳이 공개 할 필요가 있을까? - spring_board_security

  ㅇ 인증을 데이터베이스를 통해 할 수 있다.

  ㅇ 반드시 정해져있는는 테블명과 컬럼명으로 해야된다.

    ㅁ sql문을 하지 않아도 된다.

SQL> create table users
  2  (username varchar2(50) not null primary key,
  3  password varchar2(50) not null,
  4  enavled char(1));

테이블이 생성되었습니다.

SQL> create table authorities
  2  (username varchar2(50) not null,
  3  authority varchar2(50) not null);

테이블이 생성되었습니다.

SQL> insert into users values ('user1', '123', '1');

1 개의 행이 만들어졌습니다.

SQL> COMMIT

SQL> insert into authorities values('LJY','ROLE_USER');

1 개의 행이 만들어졌습니다.

SQL> COMMIT

- 인증 설정에 db 관련된 인증을 추가

  ㅇ spring 설정 파일 읽는 순서가 있음 => dataSource를 현재 설정파일로 이동

  ㅇ 기존 아이디 비밀번호 인증은 삭제

<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName"
			value="oracle.jdbc.driver.OracleDriver" />
		<property name="url"
			value="jdbc:oracle:thin:@192.168.0.35:1521:orcl" />
		<property name="username" value="scott" />
		<property name="password" value="tiger" />
	</bean>
    
<security:jdbc-user-service data-source-ref="dataSource"/>

- 패스워드 반듣시 암호화 시켜 스프링 버젼업

password encoder라는 것을 반드시 추가해야됨

{noop} password encoder가 없어도 됬으나 db를 쓸경우에는 반드시 추가 해야됨.

security 패키지 및 custompasswordencoder 클래스 생성

xml에 입력하면 아이디 비밀번호가 공개되어있음 => db를 통해서 접속 할 것임

스피링 5.0이 되면서 암호화방식이 필수로 해야 됨

현재는 그냥 리턴하게 해놨음

 

 

package kr.co.jhta.board.security;

import org.springframework.security.crypto.password.PasswordEncoder;

public class CunstomPasswordEncoder implements PasswordEncoder {

	@Override
	public String encode(CharSequence rawPassword) {
		System.out.println("패스워드 : " +rawPassword);
		return rawPassword.toString();
	}
	@Override
	public boolean matches(CharSequence rawPassword, String encodedPassword) {
		// 입력된 패스워드와 인코딩 패스워드가 동일한지 여부를 리턴	
		return rawPassword.toString().equals(encodedPassword);
	}
}
<!-- 비밀번호 암호화 bean  -->
		<bean id="passwordEncoder" class="kr.co.jhta.board.security.CunstomPasswordEncoder" />
<security:password-encoder ref="passwordEncoder"/>
<a href="list"><input type="button" value="목록" /></a>
<!-- 속성을 변수로 만든 것  -->
<sec:authentication property="principal" var="pinfo"/>
<!-- 자기 자신의 아이디와 작성자와 같을 경우에만 버튼이 사용자에게 보여진다.  -->
<!-- 배열 형태로 담겨 있다.  -->
<!-- c:out 화면에 system.out.println 값을 출력하는 것 -->
<c:out value="${pinfo.authorities}"></c:out>
<c:forEach var="p" items="${pinfo.authorities}">				
<c:if test="${pinfo.username eq dto.writer || p eq 'ROLE_ADMIN'}">
<a href="modify?bno=${dto.bno}"><input type="button" value="수정" /></a>
<a href="delete?bno=${dto.bno}"><input type="button" value="삭제" /></a>
</c:if>
</c:forEach>

- 사용자의 속성을 볼 수 있는 security 속성

 

 

 

 

 

 

 

복습 할 것

 

whois검색 : 도메인을 누가 가지고 있는 확인 할 수 있음

 

 

 

tip.

 

 

 

 

용어

 

 

 

 

 

 

 

'자바 풀스택 교육 > Spring' 카테고리의 다른 글

Spring 교육정리 15일차  (0) 2020.02.25
Spring 교육정리 13일차  (0) 2020.02.21
Spring 교육정리 12일차  (0) 2020.02.20
Spring 교육정리 11일차  (0) 2020.02.19
Spring 교육정리 9일차  (0) 2020.02.18

+ Recent posts