Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- 정수형타입
- 예외처리
- abstract
- exception
- Java
- EnhancedFor
- 예외미루기
- 제네릭
- oracle
- 컬렉션 타입
- 한국건설관리시스템
- 추상메서드
- 다형성
- GRANT VIEW
- NestedFor
- 대덕인재개발원
- 오라클
- 객체 비교
- 자동차수리시스템
- 자바
- 인터페이스
- cursor문
- 생성자오버로드
- 컬렉션프레임워크
- 어윈 사용법
- 사용자예외클래스생성
- 환경설정
- 메소드오버로딩
- 집합_SET
- 참조형변수
Archives
- Today
- Total
거니의 velog
231212_SPRING 2 (15-1) 본문
package kr.or.ddit.controller;
public class SecurityController {
/*
* [ 18장 : 스프링 시큐리티 ]
*
* 1. 스프링 시큐리티 소개
*
* - 애플리케이션에서 보안 기능을 구현하는데 사용되는 프레임워크이다.
* - 스프링 시큐리티는 필터 기반으로 동작하기 때문에 스프링 MVC와 분리되어 동작한다.
*
* # 기본 보안 기능
* - 인증 (Authentocation)
* > 애플리케이션 사용자의 정당성을 확인한다.
*
* - 인가 (Authorization)
* > 애플리케이션의 리소스나 처리에 대한 접근을 제어한다.
*
* # 시큐리티 제공 기능
* - 세션 관리
* - 로그인 처리
* - CSRF 토큰 처리
* - 암호화 처리
* - 자동 로그인
*
* *** CSRF 용어 설명
* - 크로스 사이트 요청 위조는 웹 사이트 취약점 공격의 하나로, 사용자가 자신의 의지와는 무관하기 공격자가
* 의도한 행위(수정, 삭제, 등록 등)를 특정 웹 사이트에 요청하게 하는 공격을 말한다.
*
* > CSRF 공격을 대비하기 위해서는 스프링 시큐리티 CSRF Token을 이용하여 인증을 진행한다.
*
* # 시큐리티 인증 구조
*
* 클라이언트에서 타겟으로 들어가기 위해서 요청을 진행한다. 이 때 타겟에 설정되어 있는 요청 접근 권한이 '사용자'
* 등급일 때로 설정되어 있다고 가정하자. 타겟으로 접근하기 위한 요청을 날렸고 요청 안에 사용자 등급에 해당하는 인가
* 정보가 포함되어 있지 않으면 스프링 시큐리티는 인증을 진행할 수 있도록 인증 페이지(로그인 페이지)를 제공하여
* 사용자에게 인증을 요청한다. 사용자는 아이디, 비밀번호를 입력 후 인증을 요청한다.
* 클라이언트에서 서버로 요청한 HttpServletRequest의 요청 객체를 AuthenticationFilter가 요청을
* 인터셉터 하는데, UsernamePasswordAuthenticationToken을 통해 인증을 진행할 토큰을 만들어
* AuthenticationManager에게 위임한다. 넘겨 받은 id, pw를 이용해 인증을 진행하는 데 성공시,
* Authentication 객체 생성과 성공을 전달하고, 그렇지 않으면 Exception 에러를 발생시킨다.
* 인증에 성공 후, 만들어진 Authentication 객체를 AuthenticationProvider에게 전달하고
* UserDetailService에게 넘겨받은 Authentication 객체 정보를 이용해서 Database에 일치하는 회원 정보
* 를 조회하여 꺼낸다. 꺼낸 정보를 UserDetails로 만들고 최종 User 객체에 회원 정보를 등록한다.
* 등록이 되면서 User Session 정보가 생성된다. 이후 스프링 시큐리티 내 SecurityContextHolder
* (시큐리티 인메모리)에 UserDetail 정보를 저장한다. 그리고 최종적으로 JSESSIONID가 유효한지를 검증 후
* 인증을 완료 후 타겟 정보로 넘어가도록 돕는다.
*
* 2. 스프링 시큐리티 설정
*
* # 환경 설정
* - 의존 라이브러리 설정(pom.xml 설정)
* > spring-security-web
* > spring-security-config
* > spring-security-core
* > spring-security-taglibs
*
* # 웹 컨테이너 설정(web.xml)
* - 스프링 시큐리티가 제공하는 서블릿 필터 클래스를 서블릿 컨테이너에 등록하나.
* - 스프링 시큐리티는 필터 기반이므로 시큐리티 필터체인을 등록한다.
* > context-param의 param-value 추가
* (추가 파라미터: /WEB-INF/spring/security-context.xml)
* > SpringSecurityFilterChain 추가
*
* # 스프링 시큐리티 설정
*
* - 스프링 시큐리티 컴포넌트를 빈으로 정의한다.
* - spring/security-context.xml 설정
*
* # 웹 화면 접근 정책
*
* - 웹 화면 접근 정책을 정한다. (테스트를 위한 각 화면 당 접근 정책을 설정한다.)
*
* 대상 | 화면 | 접근 정책
* ------------------------------------------------------------
* 일반 게시판 | 목록 화면 | 모두가 접근 가능하다.
* | 등록 화면 | 로그인한 회원만 접근 가능하다.
* ------------------------------------------------------------
* 공지사항 게시판 | 목록 화면 | 모두가 접근 가능하다
* | 등록 화면 | 로그인한 관리자만 접근 가능하다.
* ------------------------------------------------------------
*
* # 화면 설명
* - 컨트롤러
* > controller/SecurityBoardController
* > controller/SecurityNoticeController
*
* - 화면
* > board/list, register
* > notice/list, register
*
* 3. 접근 제한 설정
*
* - 시큐리티 설정을 통해서 특정 URI에 접근을 제한할 수 있다.
*
* # 환경 설정
*
* - 스프링 시큐리티 설정
* > URI 패턴으로 접근 제한을 설정한다.
* > security-context.xml 설정
* <security:intercept-uri pattern="/board/list" access="permitAll" />
* <security:intercept-uri pattern="/board/register" access="hasRole('ROLE_MEMBER')" />
* > notice도 마찬가지로 설정...
*
* # 화면 설명
* - 일반 게시판 목록 화면(모두 접근 가능하도록 되어 있음 : permitAll)
* - 일반 게시판 등록 화면(회원 권한을 가진 사용자만 접근 가능 : hasRole('ROLE_MEMBER'))
* > 접근 제한에 걸려 스프링 시큐리티가 기본적으로 제공하는 로그인 페이지로 이동한다.
* - 공지사항 게시판 목록 화면(모두 접근 가능하도록 되어 있음 : permitAll)
* - 공지사항 게시판 등록 화면(관리자 권한을 가진 사용자만 접근 가능 : hasRole('ROLE_ADMIN'))
* > 접근 제한에 걸려 스프링 시큐리티가 기본적으로 제공하는 로그인 페이지로 이동한다.
*
* 4. 로그인 처리
*
* - 메모리 상에 아이디와 패스워드를 지정하고 로그인을 처리한다.
* - 스프링 시큐리티 5버전부터는 패스워드 암호화 처리기를 반드시 이용하도록 변경 되었다.
* - 암호화 처리기를 사용하지 않도록 "{noop}" 문자열을 비밀번호 앞에 사용한다.
*
* # 환경 설정
*
* - 스프링 시큐리티 설정
* > security-context.xml 설정
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="member" password="{noop}1234" authorities="ROLE_MEMBER" />
<security:user name="admin" password="{noop}1234" authorities="ROLE_ADMIN" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
*
* # 화면 설명
*
* - 일반 게시판 등록 화면
* > 접근 제한에 걸려 스프링 시큐리티가 기본적으로 제공하는 로그인 페이지가 연결되고,
* 일반 회원 등급인 ROLE_MEMBER 권한을 가진 member 계정으로 로그인 후 해당 페이지로 접근 가능.
* - 공지사항 게시판 등록 화면
* > 접근 제한에 걸려 스프링 시큐리티가 기본적으로 제공하는 로그인 페이지가 연결되고,
* 관리자 등급인 ROLE_ADMIN 권한을 가진 admin 계정으로 로그인 후 해당 페이지로 접근 가능.
*
* 5. 접근 거부 처리
*
* - 접근 거부가 발생한 상황을 처리하는 접근 거부 처리자의 URI를 지정할 수 있다.
*
* # 환경 설정
*
* - 스프링 웹 설정(servlet-context.xml 설정)
* > <context:component-scan base-package="kr.or.ddit.security..." />
* ** 필요에 의한 패키지 라인을 bean으로 등록하여 사용해야 할 때 스프링 웹 설정에서 base-package를 설정할 수 있다.
*
* - 스프링 시큐리티 설정(security-context.xml 설정)
* > 접근 거부 처리자의 URI를 지정
* > <security:access-denied-handler error-page="/accessError/" />
*
* # 접근 거부 처리
*
* - 접근 거부 처리 컨트롤러 작성
* > security/CommonController
*
* # 화면 설명
* - 일반 게시판 등록 페이지
* > 접근 제한에 걸려 스프링 시큐리티가 제공하는 로그인 페이지가 나타나고, 회원 권한을 가진 계정으로
* 접근 시 접근 가능
* - 공지사항 게시판 등록 페이지
* > 접근 제한에 걸려 스프링 시큐리티가 제공하는 로그인 페이지가 나타나고, 회원 권한을 가진 계정으로
* 접근 시에 공지사항 게시판 등록 페이지는 관리자 권한으로만 접근 가능하므로 접근이 거부된다.
* 이 때, access-denied-handler로 설정되어 있는 URI로 이동하고 해당 페이지에서 접근이
* 거부 되었을 떄 보여질 페이지의 정보가 나타난다.
*
* 6. 사용자 정의 접근 거부 처리자
*
* - 접근 거부가 발생한 상황에 단순 메시지 처리 이상의 다양한 처리를 하고 싶다면 AccessDeniedHandler를 직접 구현한다.
*
* # 환경 설정
* - 스프링 시큐리티 설정(security-context.xml 설정)
* > id가 'customAccessDenied'를 가진 빈을 등록한다.
* > <security:access-denied-handler ref="customAccessDenied" />
*
* # 접근 거부 처리자 클래스 정의
* - CustomAccessDeniedHandler 클래스 정의
* > AccessDeniedHandler 인터페이스를 참조받아서 handle 메소드를 재정의하여 사용한다.
* 우리는 접근이 거부되었을 때 빈으로 등록해 둔 CustomAccessDeniedHandler 클래스가 발동해 해당 메소드가
* 실행되고 response 내장 객체를 활용하여 /accessError URL로 이동하여 접근 거부시 보여질 페이지로 이동하지만
* 이곳에서 더 많은 로직을 처리할 수도 있다. (request, response 내장 객체를 이용하여 다양한 처리 가능)
*
* # 화면 설명
* - 일반 게시판 등록 페이지
* > 접근 제한에 걸려 스프링 시큐리티가 제공하는 로그인 페이지가 나타나고, 회원 권한을 가진 계정으로 접근 시 접근 가능
* - 공지사항 게시판 등록 페이지
* > 접근 제한에 걸려 스프링 시큐리티가 제공하는 로그인 페이지가 나타나고, 회원 권한을 가진 계정으로 접근 시에 공지사항
* 게시판 등록 페이지는 관리자 권한만 접근 가능하므로 접근이 거부된다. 이 때, access-denied-handler로 설정
* 되어 있는 ref 속성에 부여된 클래스 메소드로 이동하고 해당 페이지에서 접근이 거부되었을 때 페이지의 정보가 나타난다.
*
* 7. 사용자 정의 로그인 페이지
*
* - 기본 로그인 페이지가 아닌 사용자가 직접 정의한 로그인 페이지를 사용한다.
*
* # 환경 설정
* - 스프링 시큐리티 설정(security-context.xml 설정)
*
* <security:form-login login-page="/login" /> 설정
* : 사용자가 직접 만든 로그인 페이지로 이동할 '/login' URL을 가지고 있는 컨트롤러 메소드를 정의
*
* # 로그인 페이지 정의
*
* - 사용자가 정의한 로그인 컨트롤러
* > controller 패키지 안에 LoginController 생성
* - 사용자가 정의한 로그인 뷰
* > views/loginForm.jsp
*
* ** 시큐리티에서 제공하는 기본 로그인 페이지로 이동하지 않고, 사용자가 정의한 로그인 페이지의 URI를 요청하여
* 해당 페이지에서 권한을 체크하도록 한다. 인증이 완료되면 최초의 요청된 target URI로 이동한다.
* 그렇지 않은 경우 사용자가 만들어놓은 접근 거부 페이지로 이동한다.
*
* 8. 로그인 성공 처리
*
* - 로그인을 성공한 후에 로그인 이력 로그를 기록하는 등의 동적을 하고 싶은 경우가 있다.
* 이런 경우 AuthenticationSuccessHandler라는 인터페이스를 구현해서 로그인 성공 처리자로 지정할 수 있다.
*
* # 환경 설정
* - customLoginSuccess Bean 등록
* <security:form-login login-page="/login" authentication-success-handler-ref="customLoginSuccess" />
*
* # 로그인 성공 처리자 클래스 정의
* - 로그인 성공 처리자
* > SavedRequestAwareAuthenticationSuccessHandler는 AuthenticationSuccessHandler의 구현 클래스이다.
* 인증 전에 접근을 시도한 URL로 리다이렉트하는 기능을 가지고 있으며 스프링 시큐리티에서 기본적으로
* 사용되는 구현 클래스이다.
* - 로그인 성공 처리자2
* > AuthenticationSuccessHandler 인터페이스를 직접 구현하여 인증 전에 접근을 시도한 URL로
* 리다이렉트하는 기능을 구현한다.
*
* # 화면 설명
* - 일반 게시판 등록 화면
* > 사용자가 정의한 로그인 페이지에서 회원 권한에 해당하는 계정으로 로그인 시, 성공했다면 성공 처리자인
* CustomLoginSuccess 클래스로 넘어가 넘겨받은 파라미터들 중 authentication 안에
* principal로 User 정보를 받아서 username과 password를 출력한다.
* (출력 정보는 로그인 성공 시 인증된 회원 정보이다.)
*
* 9. 로그아웃 처리
*
* - 로그아웃을 위한 URI를 지정하고, 로그아웃 처리 후에 별도의 작업을 하기 위해서 사용자가 직접 구현한 처리자를
* 등록할 수 있다.
*
* # 환경 설정
*
* - 스프링 시큐리티 설정(security-context.xml 설정)
* > <security:logout logout-url="/logout" invalidate-session="true" />
*
* ** logout 경로를 스프링에서 제공하는 /logout 경로로 설정한다.
* logout 처리 페이지에서도 action 경로를 /logout으로 설정한다.
*
* 10. JDBC를 이용한 인증/인가 처리
*
* - 지정한 형식으로 테이블을 생성하면 JDBC를 이용해서 인증/인가를 처리할 수 있다.
* - 생성할 테이블은 사용자를 관리하는 테이블(users)과 권한을 관리하는 테이블이다.
*
* # 데이터베이스 테이블 준비
*
* - users, authorities 테이블 준비
*
* # 환경설정
*
* - 의존 라이브러리 설정
* > 데이터베이스 관련 라이브러리를 추가한다.
* > 기존 데이터베이스 연결을 위한 라이브러리를 가져와 등록(pom.xml)
*
* # 스프링 설정(root-context.xml 설정)
*
* - 데이터 소스 설정(기존 설정)
*
* # 스프링 시큐리티 설정(security-context.xml 설정)
*
* - customPasswordEncoder 빈 등록 진행
* <security:authentication-manage> 태그 설정
*
* # 비밀번호 암호화 처리기 클래스 정의
* - 비밀번호 암호화 처리기
* 스프링 시큐리티 5부터는 기본적으로 PasswordEncoder를 지정해야 하는데, 제대로 하려면 생성된 사용자 테이블(users)
* 에 비밀번호를 암호화하여 저장해야 한다. 테스트를 위해서 생성한 데이터는 암호화를 처리하지 않으므로 로그인 하면
* 당연히 로그인 에러가 발생할 것이다. (암호화된 비밀번호가 날아가는게 아니라서)
* 그래서 암호화를 하지 않는 PasswordNoOpPasswordEncoder를 직접 구현하여 지정하면 로그인 시 암호화를 고려하지
* 않으므로 로그인이 정상적으로 이루어지는 걸 확인할 수 있다.
* ** PasswordEncoder를 참조받아서 우리가 원하는 로직으로 변경해서 사용(암호화를 사용하지 않는 루트로)
*
* 11. 사용자 테이블 이용한 인증/인가 처리
*
* - 스프링 시큐리티가 기본적으로 이용하는 테이블 구조를 그대로 생성해서 사용해도 되지만, 기존에 구축된 회원 테이블이 있다면
* 약간의 작업으로 기존 테이블을 활용할 수 있다.
*
* # 데이터베이스 테이블 준비
* - member, member_auth 테이블 준비
*
* # 환경 설정
* - 스프링 시큐리티 설정(security-context.xml 설정)
* > bcryptPasswordEncoder 빈 등록 진행
* > <security:jdbc-user-service> 태그 설정
* > <security:password-encoder> 태그 설정
*
* # 쿼리 정의
* - 인증할 때 필요한 쿼리
* > select user_id, user_pw, enabled from member where user_id = ?
*
* - 권한을 확인할 때 필요한 쿼리
* > select m.user_id, ma.auth from member m, member_auth ma where ma.user_no = m.user_no and m.user_id = ?
*
* # BCryptPasswordEncoder 클래스를 이용하여 직접 encode된 비밀번호를 찾아 데이터베이스에 셋팅한다.
* - 12번 UserDetails 정보를 등록하면서 같이 진행
*
* - BCryptPasswordEncoder 클래스를 활용한 단방향 비밀번호 암호화
* > encode() 메소드를 통해서 SHA-2 방식의 8바이트 Hash 암호를 매번 랜덤하게 생성한다.
* > 똑같은 비밀번호를 입력하더라도 암호화되는 문자열은 매번 다른 문자열을 반환한다.
*
* 비밀번호를 입력하면 암호화된 비밀번호로 인코딩하는데, 암호화된 비밀번호와 DB 테이블에 있는 암호화된 비밀번호가
* 일치한지를 파악 후 일치하면 로그인 성공으로 다음 스탭을 진행한다.
* > BCryptPasswordEncoder 클래스의 encode() 메소드를 통해 만들어지는 암호화된 해쉬 다이제스트들은 입력한
* 비밀번호 문자에 해당하는 수십억개의 다이제스트들 중에서 일치하는 다이제스트가 존재할 경우 비밀번호의 일치로 보고
* 인증을 성공시켜준다.
*
* 12. UserDetailsService 재정의
*
* - 스프링 시큐리티의 UserDetailsService를 구현하여 사용자 상세 정보를 얻어오는 메서드를 재정의한다.
*
* # 환경 설정
* - 의존 라이브러리 설정(pom.xml) 설정
* > 데이터베이스 관련 라이브러리
* - 스프링 시큐리티 설정(security-context.xml 설정)
* > customUserDetailsService 빈 등록
* > security:authentication-provider 태그 설정
*
* # 클래스 재정의
* - UserDetailsService 재정의
* > security/CustomUserDetailsService 클래스 생성
* > 기존 사용중인 read를 기반으로 한 readById 재정의
* > CustomUserDetailsService 클래스 내 loadUserByUsername 메소드에서 인코딩 된 비밀번호 확인 후
* 데이터베이스 비밀번호 수정
* > Member 테이블 내 특정 계정들 비밀번호를 암호화된 비밀번호로 수정한다.
*
* 13. 스프링 시큐리티 표현식
*
* - 스프링 시큐리티를 이용하면 인증 및 권한 정보에 따라 화면을 동적으로 구성할 수 있고,
* 로그인 한 사용자 정보를 보여줄 수도 있다.
*
* # 공동 표현식
*
* - hasRole([role])
* > 해당 롤이 있으면 true
* - hasAnyRole([role1, role2])
* > 여러 롤들 중에서 하나라도 해당하는 롤이 있으면 true
* - principal
* > 인증된 사용자의 사용자 정보(UserDetails 인터페이스를 구현한 클래스의 객체)를 의미
* - authentication
* > 인증된 사용자의 인증 정보(Authentication 인터페이스를 구현한 클래스의 객체)fmf dmlal
* - permitAll
* > 모든 사용자에게 허용
* - denyAll
* > 모든 사용자에게 거부
* - isAnonymous()
* > 익명의 사용자의 경우(로그인을 하지 않은 경우도 해당)
* - isAuthenticated()
* > 인증된 사용자면 true를 반환
* - isFullyAuthenticated()
* > Remember-me로 인증된 것이 아닌 일반적인 방법으로 인증된 사용자인 경우 true
*
* # 표현식 사용
*
* - 표현식을 이용하여 동적 화면 구성
* > home.jsp 수정
* : 표현식을 이용한 내용 추가
*
* - 로그인한 사용자 정보 보여주기
* > views/board/register.jsp 수정
* > views/notice/register.jsp 수정
*
* 14. 자동 로그인
*
* - 로그인하면 특정 시간 동안 다시 로그인 할 필요가 없는 기능이다.
* - 스프링 시큐리티는 메모리나 데이터베이스를 사용하여 처리한다.
* - 기능을 구현하기 위해 <security:remember-me> 태그를 이용하여 시큐리티 설정 파일을 수정한다.
*
* # 데이터베이스 테이블
* - persistent_logins 테이블 준비
*
* # 환경 설정
* - 스프링 시큐리티 설정(security-context.xml 설정)
* <security:remember-me data-source-ref="dataSource" token-validaty="604800" /> 태그 설정
* <security:logout logout-url="/logout" invalidate-session="true"
* delete-cookies="remember-me, JSESSION_ID" /> 태그 설정
*
* # 자동 로그인
* - 로그인 상태 유지 체크박스 추가
* > loginForm.jsp 수정 (자동 로그인 체크 박스 만들기)
*
* # 자동 로그인 시, 만들어지는 쿠키 정보들
* - JSESSIONID와 remember-me 쿠키가 만들어진다.
* - JSESSIONID를 삭제 후, 다시 로그인을 진행하더라도 로그인 후 진행될 페이지가 정상적으로 나타나는 것을 확인할 수 있음
* > 자동 로그인이 remember-me 쿠키에 의해서 데이터베이스에 저장되는 각 hash token 값에 의해 재설정됨을 확인
*/
}
-----------------------------------------
-- 스프링 시큐리티
-----------------------------------------
create table users(
username varchar2(50) not null,
password varchar2(50) not null,
enabled char(1) default '1' null,
constraint pk_users primary key(username)
);
create table authorities(
username varchar2(50) not null,
authority varchar2(50) not null,
constraint fk_authorities_users_username foreign key(username)
references users(username)
);
insert into users values('user00', '1234', '1');
insert into users values('member00', '1234', '1');
insert into users values('admin00', '1234', '1');
insert into authorities values('user00', 'ROLE_USER');
insert into authorities values('member00', 'ROLE_MEMBER');
insert into authorities values('admin00', 'ROLE_MEMBER');
insert into authorities values('admin00', 'ROLE_ADMIN');
commit;
[pom.xml]
<!-- 데이터베이스 의존관계 시작 -->
<!-- XML로 쿼리를 작성하게 해주는 라이브러리 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!-- 스프링과 mybatis를 연동하게 해주는 라이브러리 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.4</version>
</dependency>
<!-- 스프링에서 JDBC(Java Database Connectivity) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!--
dbcp : database connection pool > 커넥션 객체를 미리 만들어놓고 쓰고 반납
미리 커넥션 객체를 여러 개 만들어 놓고 가용해야 할 때 커넥션을 들고 가서 사용하다가 다시 반납
최근에는 hikaricp를 사용하는 경우도 있음
-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.7.0</version>
</dependency>
<!-- 로깅을 위한 라이브러리, 쿼리를 console이나 파일 로드로 볼 수 있다. -->
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4</artifactId>
<version>1.16</version>
</dependency>
<!-- 오라클 데이터베이스 라이브러리 -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>21.1.0.0</version>
</dependency>
<!-- 데이터베이스 의존관계 끝 -->
[root-context.xml]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<!-- dataSource : 데이터베이스와 관련된 정보를 설정한다. -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="spring2" />
<property name="password" value="java" />
</bean>
<!--
데이터베이스와 연결을 맺고 끊어질 때까지의 라이프 사이클을 관리해주는 SqlSessionTemplate 객체를 생성한다.
1) dataSource
2) Mapper xml 위치 지정
3) 마이바티스 설정 위치 지정
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:/sqlmap/**/*_SQL.xml" />
<property name="configLocation" value="/WEB-INF/mybatisAlias/mybatisAlias.xml" />
</bean>
<!--
데이터베이스에 쿼리를 실행시키는 객체
이 객체를 통해서 query를 실행한다.
-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" index="0" ref="sqlSessionFactory" />
</bean>
<!--
Mapper 인터페이스 설정
개발자가 직접 DAO를 설정하지 않아도 자동으로 Mapper 인터페이스를 활용하는 객체를 생성하게 된다.
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="kr.or.ddit.mapper" />
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--
[마이바티스] 스프링에서 "_"를 사용한 컬럼명 사용 시(BOOK 테이블의 BOOK_ID와 같은 컬럼)
카멜케이스로 읽어주는 역할(bookId와 같이)
ex) 테이블 컬럼명이 member_id인 경우 jsp 화면 단에서 이 값을 사용시 memberId로 사용할 수 있다.
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<!-- <typeAlias type="kr.or.ddit.vo.Board" alias="board"/> -->
</typeAliases>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="Blank">
</mapper>
[security-context.xml]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 6. 사용자 정의 접근 거부 처리자 Bean 추가 -->
<bean id="customAccessDenied" class="kr.or.ddit.security.CustomAccessDeniedHandler"></bean>
<bean id="customLoginSuccess" class="kr.or.ddit.security.CustomLoginSuccessHandler"></bean>
<bean id="customPasswordEncoder" class="kr.or.ddit.security.CustomNoOpPasswordEncoder"></bean>
<security:http>
<!-- 3. 접근 제한 설정 : URL 패턴으로 접근 제한을 설정한다. -->
<security:intercept-url pattern="/board/list" access="permitAll" />
<security:intercept-url pattern="/board/register" access="hasRole('ROLE_MEMBER')" />
<security:intercept-url pattern="/notice/list" access="permitAll" />
<security:intercept-url pattern="/notice/register" access="hasRole('ROLE_ADMIN')" />
<!-- 3. 접근 제한 설정 끝 -->
<!--
폼 기반 인증 기능을 사용한다. : 사용자 정의 로그인 페이지 추가(login-page),
로그인 성공 처리 추가 : 로그인 성공 후 처리를 담당하는 처리자로 지정(customLoginSuccess)
-->
<security:form-login login-page="/login" authentication-success-handler-ref="customLoginSuccess" />
<!-- 5. 접근 거부 처리자 : 접근 거부 처리자의 URI를 지정 -->
<!-- <security:access-denied-handler error-page="/accessError" /> -->
<!-- 6. 사용자 정의 접근 거부 처리자 추가 : customAccessDenied를 접근 거부 처리자로 등록 -->
<security:access-denied-handler ref="customAccessDenied" />
<!-- 9. 로그아웃 처리 : 로그아웃 처리를 위한 URI를 지정하고, 로그아웃한 후에 세션을 무효화한다. -->
<security:logout logout-url="/logout" invalidate-session="true" />
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="member" password="{noop}1234" authorities="ROLE_MEMBER" />
<security:user name="admin" password="{noop}1234" authorities="ROLE_ADMIN" />
</security:user-service>
<security:jdbc-user-service data-source-ref="dataSource"/>
<security:password-encoder ref="customPasswordEncoder" />
</security:authentication-provider>
</security:authentication-manager>
</beans>
package kr.or.ddit.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.password.PasswordEncoder;
public class CustomNoOpPasswordEncoder implements PasswordEncoder {
private static final Logger log = LoggerFactory.getLogger(CustomNoOpPasswordEncoder.class);
@Override
public String encode(CharSequence rawPassword) {
log.info("before encode : " + rawPassword);
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
log.info("matches : " + rawPassword + " :::: " + encodedPassword);
return rawPassword.toString().equals(encodedPassword);
}
}
- http://localhost/notice/register
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 6. 사용자 정의 접근 거부 처리자 Bean 추가 -->
<bean id="customAccessDenied" class="kr.or.ddit.security.CustomAccessDeniedHandler"></bean>
<bean id="customLoginSuccess" class="kr.or.ddit.security.CustomLoginSuccessHandler"></bean>
<bean id="customPasswordEncoder" class="kr.or.ddit.security.CustomNoOpPasswordEncoder"></bean>
<security:http>
<!-- 3. 접근 제한 설정 : URL 패턴으로 접근 제한을 설정한다. -->
<security:intercept-url pattern="/board/list" access="permitAll" />
<security:intercept-url pattern="/board/register" access="hasRole('ROLE_MEMBER')" />
<security:intercept-url pattern="/notice/list" access="permitAll" />
<security:intercept-url pattern="/notice/register" access="hasRole('ROLE_ADMIN')" />
<!-- 3. 접근 제한 설정 끝 -->
<!--
폼 기반 인증 기능을 사용한다. : 사용자 정의 로그인 페이지 추가(login-page),
로그인 성공 처리 추가 : 로그인 성공 후 처리를 담당하는 처리자로 지정(customLoginSuccess)
-->
<security:form-login login-page="/login" authentication-success-handler-ref="customLoginSuccess" />
<!-- 5. 접근 거부 처리자 : 접근 거부 처리자의 URI를 지정 -->
<!-- <security:access-denied-handler error-page="/accessError" /> -->
<!-- 6. 사용자 정의 접근 거부 처리자 추가 : customAccessDenied를 접근 거부 처리자로 등록 -->
<security:access-denied-handler ref="customAccessDenied" />
<!-- 9. 로그아웃 처리 : 로그아웃 처리를 위한 URI를 지정하고, 로그아웃한 후에 세션을 무효화한다. -->
<security:logout logout-url="/logout" invalidate-session="true" />
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<!-- <security:user-service>
<security:user name="member" password="{noop}1234" authorities="ROLE_MEMBER" />
<security:user name="admin" password="{noop}1234" authorities="ROLE_ADMIN" />
</security:user-service> -->
<security:jdbc-user-service data-source-ref="dataSource"/>
<security:password-encoder ref="customPasswordEncoder" />
</security:authentication-provider>
</security:authentication-manager>
</beans>
[security-context.xml]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 6. 사용자 정의 접근 거부 처리자 Bean 추가 -->
<bean id="customAccessDenied" class="kr.or.ddit.security.CustomAccessDeniedHandler"></bean>
<bean id="customLoginSuccess" class="kr.or.ddit.security.CustomLoginSuccessHandler"></bean>
<!-- <bean id="customPasswordEncoder" class="kr.or.ddit.security.CustomNoOpPasswordEncoder"></bean> -->
<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
<bean id="customUserDetailsService" class="kr.or.ddit.security.CustomUserDetailsService"></bean>
<security:http>
<!-- 3. 접근 제한 설정 : URL 패턴으로 접근 제한을 설정한다. -->
<security:intercept-url pattern="/board/list" access="permitAll" />
<security:intercept-url pattern="/board/register" access="hasRole('ROLE_MEMBER')" />
<security:intercept-url pattern="/notice/list" access="permitAll" />
<security:intercept-url pattern="/notice/register" access="hasRole('ROLE_ADMIN')" />
<!-- 3. 접근 제한 설정 끝 -->
<!--
폼 기반 인증 기능을 사용한다. : 사용자 정의 로그인 페이지 추가(login-page),
로그인 성공 처리 추가 : 로그인 성공 후 처리를 담당하는 처리자로 지정(customLoginSuccess)
-->
<security:form-login login-page="/login" authentication-success-handler-ref="customLoginSuccess" />
<!-- 5. 접근 거부 처리자 : 접근 거부 처리자의 URI를 지정 -->
<!-- <security:access-denied-handler error-page="/accessError" /> -->
<!-- 6. 사용자 정의 접근 거부 처리자 추가 : customAccessDenied를 접근 거부 처리자로 등록 -->
<security:access-denied-handler ref="customAccessDenied" />
<!-- 9. 로그아웃 처리 : 로그아웃 처리를 위한 URI를 지정하고, 로그아웃한 후에 세션을 무효화한다. -->
<security:logout logout-url="/logout" invalidate-session="true" />
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<!-- <security:user-service>
<security:user name="member" password="{noop}1234" authorities="ROLE_MEMBER" />
<security:user name="admin" password="{noop}1234" authorities="ROLE_ADMIN" />
</security:user-service> -->
<!-- 사용자 정의 테이블을 이용한 인증/인가 처리 -->
<!-- <security:jdbc-user-service data-source-ref="dataSource"/>
<security:password-encoder ref="customPasswordEncoder" /> -->
<!-- 사용자 정의 테이블을 이용한 인증/인가 처리 끝 -->
<security:jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select user_id, user_pw, enabled from member where user_id = ?"
authorities-by-username-query="select m.user_id, ma.auth from member m, member_auth ma where ma.user_no = m.user_no and m.user_id = ?" />
<security:password-encoder ref="bcryptPasswordEncoder" />
</security:authentication-provider>
</security:authentication-manager>
</beans>
package kr.or.ddit.security;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import kr.or.ddit.mapper.IMemberMapper;
import kr.or.ddit.vo.CrudMember;
import kr.or.ddit.vo.CustomUser;
public class CustomUserDetailsService implements UserDetailsService {
private static final Logger log = LoggerFactory.getLogger(CustomUserDetailsService.class);
@Inject
private BCryptPasswordEncoder bpe;
@Inject
private IMemberMapper memberMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("loadUserByUsername() 실행...!");
String password = "1234";
log.info("#### 암호화된 비밀번호 : " + bpe.encode(password));
log.info("#### 암호화된 비밀번호 : " + bpe.encode(password));
log.info("#### 암호화된 비밀번호 : " + bpe.encode(password));
log.info("#### 암호화된 비밀번호 : " + bpe.encode(password));
log.info("#### 암호화된 비밀번호 : " + bpe.encode(password));
log.info("#### 암호화된 비밀번호 : " + bpe.encode(password));
log.info("#### 암호화된 비밀번호 : " + bpe.encode(password));
log.info("Load User By Username : " + username);
// UserDetailService를 등록하는 과정에서 우리가 할 목표는 User 객체의 정보와
// 인증되어 실제로 사용될 내 id에 해당하는 회원정보를 CrudMember에 담고 그 녀석을 UserDetails 정보 안에서
// 가용할 수 있도록 만든다.
CrudMember member;
try {
member = memberMapper.readByUserId(username);
log.info("queried by member mapper : " + member);
return member == null ? null : new CustomUser(member);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
package kr.or.ddit.vo;
import java.util.Date;
import java.util.List;
public class CrudMember {
private int userNo;
private String userId;
private String userPw;
private String userName;
private Date regDate;
private Date updDate;
private List<CrudMemberAuth> authList;
public int getUserNo() {
return userNo;
}
public void setUserNo(int userNo) {
this.userNo = userNo;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserPw() {
return userPw;
}
public void setUserPw(String userPw) {
this.userPw = userPw;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getRegDate() {
return regDate;
}
public void setRegDate(Date regDate) {
this.regDate = regDate;
}
public Date getUpdDate() {
return updDate;
}
public void setUpdDate(Date updDate) {
this.updDate = updDate;
}
public List<CrudMemberAuth> getAuthList() {
return authList;
}
public void setAuthList(List<CrudMemberAuth> authList) {
this.authList = authList;
}
}
package kr.or.ddit.vo;
public class CrudMemberAuth {
private int userNo;
private String auth;
public int getUserNo() {
return userNo;
}
public void setUserNo(int userNo) {
this.userNo = userNo;
}
public String getAuth() {
return auth;
}
public void setAuth(String auth) {
this.auth = auth;
}
}
package kr.or.ddit.mapper;
import kr.or.ddit.vo.CrudMember;
public interface IMemberMapper {
public CrudMember readByUserId(String username);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kr.or.ddit.mapper.IMemberMapper">
<resultMap type="crudMember" id="memberMap">
<id property="userNo" column="user_no" />
<result property="userNo" column="user_no" />
<result property="userId" column="user_id" />
<result property="userPw" column="user_pw" />
<result property="userName" column="user_name" />
<result property="regDate" column="reg_date" />
<result property="updDate" column="upd_date" />
<collection property="authList" resultMap="authMap" />
</resultMap>
<resultMap type="crudMemberAuth" id="authMap">
<result property="userNo" column="user_no" />
<result property="auth" column="auth" />
</resultMap>
<select id="readByUserId" parameterType="string" resultMap="memberMap">
select
m.user_no, user_id, user_pw, user_name, reg_date, upd_date, a.auth
from member m left outer join member_auth a on(m.user_no = a.user_no)
where user_id = #{userId}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--
[마이바티스] 스프링에서 "_"를 사용한 컬럼명 사용 시(BOOK 테이블의 BOOK_ID와 같은 컬럼)
카멜케이스로 읽어주는 역할(bookId와 같이)
ex) 테이블 컬럼명이 member_id인 경우 jsp 화면 단에서 이 값을 사용시 memberId로 사용할 수 있다.
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<typeAlias type="kr.or.ddit.vo.CrudMember" alias="crudMember"/>
<typeAlias type="kr.or.ddit.vo.CrudMemberAuth" alias="crudMemberAuth"/>
</typeAliases>
</configuration>
package kr.or.ddit.vo;
import java.util.Collection;
import java.util.stream.Collectors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
public class CustomUser extends User {
private CrudMember member;
public CustomUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
public CustomUser(CrudMember member) {
// Java 스트림을 사용한 경우(람다 표현식)
// - 자바 버전 8부터 추가된 기능
// map : 컬렉션(List, Map, Set 등), 배열 등의 설정되어 있는 각 타입의 값들을 하나씩 참조하여 람다식으로 반복 처리할 수 있게 해준다.
// collect : Stream()을 돌려 발생되는 데이터를 가공 처리 하고 원하는 형태의 자료형으로 변환을 돕는다.
// 회원 정보 안에 들어 있는 역할명 들을 컬렉션 형태의 스트림으로 만들어서 보내준다.
// *** 람다 표현식은 복잡한 메서드 라인을 간단한 표현식으로 출력할 수 있다는 장점이 있는 대신 디버깅을 할 수 없다는 점이 단점이다.
super(member.getUserId(), member.getUserPw(), member.getAuthList().stream().map(auth -> new SimpleGrantedAuthority(auth.getAuth())).collect(Collectors.toList()));
this.member = member;
}
public CrudMember getMember() {
return member;
}
public void setMember(CrudMember member) {
this.member = member;
}
}
[security-context.xml]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 6. 사용자 정의 접근 거부 처리자 Bean 추가 -->
<bean id="customAccessDenied" class="kr.or.ddit.security.CustomAccessDeniedHandler"></bean>
<bean id="customLoginSuccess" class="kr.or.ddit.security.CustomLoginSuccessHandler"></bean>
<!-- <bean id="customPasswordEncoder" class="kr.or.ddit.security.CustomNoOpPasswordEncoder"></bean> -->
<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
<bean id="customUserDetailsService" class="kr.or.ddit.security.CustomUserDetailsService"></bean>
<security:http>
<!-- 3. 접근 제한 설정 : URL 패턴으로 접근 제한을 설정한다. -->
<security:intercept-url pattern="/board/list" access="permitAll" />
<security:intercept-url pattern="/board/register" access="hasRole('ROLE_MEMBER')" />
<security:intercept-url pattern="/notice/list" access="permitAll" />
<security:intercept-url pattern="/notice/register" access="hasRole('ROLE_ADMIN')" />
<!-- 3. 접근 제한 설정 끝 -->
<!--
폼 기반 인증 기능을 사용한다. : 사용자 정의 로그인 페이지 추가(login-page),
로그인 성공 처리 추가 : 로그인 성공 후 처리를 담당하는 처리자로 지정(customLoginSuccess)
-->
<security:form-login login-page="/login" authentication-success-handler-ref="customLoginSuccess" />
<!-- 5. 접근 거부 처리자 : 접근 거부 처리자의 URI를 지정 -->
<!-- <security:access-denied-handler error-page="/accessError" /> -->
<!-- 6. 사용자 정의 접근 거부 처리자 추가 : customAccessDenied를 접근 거부 처리자로 등록 -->
<security:access-denied-handler ref="customAccessDenied" />
<!-- 9. 로그아웃 처리 : 로그아웃 처리를 위한 URI를 지정하고, 로그아웃한 후에 세션을 무효화한다. -->
<security:logout logout-url="/logout" invalidate-session="true" />
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="customUserDetailsService">
<!-- <security:user-service>
<security:user name="member" password="{noop}1234" authorities="ROLE_MEMBER" />
<security:user name="admin" password="{noop}1234" authorities="ROLE_ADMIN" />
</security:user-service> -->
<!-- 사용자 정의 테이블을 이용한 인증/인가 처리 -->
<!-- <security:jdbc-user-service data-source-ref="dataSource"/>
<security:password-encoder ref="customPasswordEncoder" /> -->
<!-- 사용자 정의 테이블을 이용한 인증/인가 처리 끝 -->
<!--
UserDetailsService를 설정하면서 데이터베이스 연동 후 사용자가 정의한 테이블로 mapper를 통한 데이터 바인딩으로
인증/인가 진행 시 주석
-->
<!-- <security:jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select user_id, user_pw, enabled from member where user_id = ?"
authorities-by-username-query="select m.user_id, ma.auth from member m, member_auth ma where ma.user_no = m.user_no and m.user_id = ?" />
<security:password-encoder ref="bcryptPasswordEncoder" /> -->
<security:password-encoder ref="bcryptPasswordEncoder" />
</security:authentication-provider>
</security:authentication-manager>
</beans>
- 디버그 모드로 이행
- http://localhost/board/register
- http://localhost/logout
- http://localhost/board/register
[home.jsp]
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page session="false" %>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec" %>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>
Hello world!
</h1>
<P> The time on the server is ${serverTime}. </P>
<!--
로그인을 하지 않은 경우
isAnonymous() : 익명의 사용자의 경우(로그인을 하지 않은 경우도 해당)
-->
<sec:authorize access="isAnonymous()">
<a href="/login">로그인</a>
</sec:authorize>
<!--
인증된 사용자인 경우
isAuthenticated() : 인증된 사용자면 true
-->
<sec:authorize access="isAuthenticated()">
<a href="/logout">로그아웃</a>
</sec:authorize>
<div>
<a href="/board/list">Board</a>
</div>
<div>
<a href="/notice/list">Notice</a>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>BOARD REGISTER</title>
</head>
<body>
<h3>BOARD REGISTER : access to member</h3>
<sec:authentication property="principal.member.userName" var="name" />
<sec:authentication property="principal.member.userId" var="id" />
<sec:authentication property="principal.member.userPw" var="pw" />
<p>
사용자명 : ${name }<br />
아이디 : ${id }<br />
비밀번호 : ${pw }<br />
</p>
<p>principal : <sec:authentication property="principal" /></p>
<p>principal.member : <sec:authentication property="principal.member" /></p>
<p>
<sec:authorize access="hasRole('ROLE_MEMBER')">
${name }님의 역할명은 회원입니다!
</sec:authorize>
<sec:authorize access="hasRole('ROLE_ADMIN')">
${name }님의 역할명은 관리자입니다!
</sec:authorize>
</p>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>NOTICE REGISTER</title>
</head>
<body>
<h3>NOTICE REGISTER : access to admin</h3>
<sec:authentication property="principal.member.userName" var="name" />
<sec:authentication property="principal.member.userId" var="id" />
<sec:authentication property="principal.member.userPw" var="pw" />
<p>
사용자명 : ${name }<br />
아이디 : ${id }<br />
비밀번호 : ${pw }<br />
</p>
<p>principal : <sec:authentication property="principal" /></p>
<p>principal.member : <sec:authentication property="principal.member" /></p>
<p>
<sec:authorize access="hasRole('ROLE_MEMBER')">
${name }님의 역할명은 회원입니다!
</sec:authorize>
<sec:authorize access="hasRole('ROLE_ADMIN')">
${name }님의 역할명은 관리자입니다!
</sec:authorize>
</p>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>BOARD LIST</title>
</head>
<body>
<h3>BOARD LIST : access to all</h3>
<div>
<a href="/board/register">Register</a>
</div>
<div>
<a href="/">Home</a>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>NOTICE LIST</title>
</head>
<body>
<h3>NOTICE LIST : access to all</h3>
<div>
<a href="/notice/register">Register</a>
</div>
<div>
<a href="/">Home</a>
</div>
</body>
</html>
- http://localhost/
- http://localhost/board/register
- http://localhost/notice/register
create table persistent_logins(
username varchar2(64) not null,
series varchar2(64) not null,
token varchar2(64) not null,
last_used date not null,
constraint pk_persistent_logins primary key(series)
);
[security-context.xml]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 6. 사용자 정의 접근 거부 처리자 Bean 추가 -->
<bean id="customAccessDenied" class="kr.or.ddit.security.CustomAccessDeniedHandler"></bean>
<bean id="customLoginSuccess" class="kr.or.ddit.security.CustomLoginSuccessHandler"></bean>
<!-- <bean id="customPasswordEncoder" class="kr.or.ddit.security.CustomNoOpPasswordEncoder"></bean> -->
<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
<bean id="customUserDetailsService" class="kr.or.ddit.security.CustomUserDetailsService"></bean>
<security:http>
<!-- 3. 접근 제한 설정 : URL 패턴으로 접근 제한을 설정한다. -->
<security:intercept-url pattern="/board/list" access="permitAll" />
<security:intercept-url pattern="/board/register" access="hasRole('ROLE_MEMBER')" />
<security:intercept-url pattern="/notice/list" access="permitAll" />
<security:intercept-url pattern="/notice/register" access="hasRole('ROLE_ADMIN')" />
<!-- 3. 접근 제한 설정 끝 -->
<!--
폼 기반 인증 기능을 사용한다. : 사용자 정의 로그인 페이지 추가(login-page),
로그인 성공 처리 추가 : 로그인 성공 후 처리를 담당하는 처리자로 지정(customLoginSuccess)
-->
<security:form-login login-page="/login" authentication-success-handler-ref="customLoginSuccess" />
<!-- 5. 접근 거부 처리자 : 접근 거부 처리자의 URI를 지정 -->
<!-- <security:access-denied-handler error-page="/accessError" /> -->
<!-- 6. 사용자 정의 접근 거부 처리자 추가 : customAccessDenied를 접근 거부 처리자로 등록 -->
<security:access-denied-handler ref="customAccessDenied" />
<!--
14. 자동 로그인 적용
- 데이터 소스를 지정하고 테이블을 이용해서 기존 로그인 정보를 기록
- 쿠키의 유효 시간을 지정한다. (604800 : 7일)
-->
<security:remember-me data-source-ref="dataSource" token-validity-seconds="604800" />
<!-- 9. 로그아웃 처리 : 로그아웃 처리를 위한 URI를 지정하고, 로그아웃한 후에 세션을 무효화한다. -->
<security:logout logout-url="/logout" invalidate-session="true"
delete-cookies="remember-me, JSESSION_ID" />
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="customUserDetailsService">
<!-- <security:user-service>
<security:user name="member" password="{noop}1234" authorities="ROLE_MEMBER" />
<security:user name="admin" password="{noop}1234" authorities="ROLE_ADMIN" />
</security:user-service> -->
<!-- 사용자 정의 테이블을 이용한 인증/인가 처리 -->
<!-- <security:jdbc-user-service data-source-ref="dataSource"/>
<security:password-encoder ref="customPasswordEncoder" /> -->
<!-- 사용자 정의 테이블을 이용한 인증/인가 처리 끝 -->
<!--
UserDetailsService를 설정하면서 데이터베이스 연동 후 사용자가 정의한 테이블로 mapper를 통한 데이터 바인딩으로
인증/인가 진행 시 주석
-->
<!-- <security:jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select user_id, user_pw, enabled from member where user_id = ?"
authorities-by-username-query="select m.user_id, ma.auth from member m, member_auth ma where ma.user_no = m.user_no and m.user_id = ?" />
<security:password-encoder ref="bcryptPasswordEncoder" /> -->
<security:password-encoder ref="bcryptPasswordEncoder" />
</security:authentication-provider>
</security:authentication-manager>
</beans>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>LOGIN FORM</title>
</head>
<body>
<h1>Login</h1>
<h2>${error }</h2> <!-- 에러 발생 시, 출력할 메세지 -->
<h2>${logout }</h2> <!-- 로그아웃 발생 시, 출력할 메세지 -->
<!-- security를 적용 후 데이터를 전송 시 csrfInput으로 시큐리티 토큰을 전송해야 한다. -->
<form action="/login" method="post">
username : <input type="text" name="username" /><br />
password : <input type="text" name="password" /><br />
<input type="checkbox" name="remember-me" /> Remember Me <br />
<button type="submit">로그인</button>
<sec:csrfInput />
</form>
</body>
</html>
- http://localhost/
'대덕인재개발원 > 대덕인재개발원_웹기반 애플리케이션' 카테고리의 다른 글
231213_SPRING 2 (16-1) (0) | 2023.12.12 |
---|---|
231212_SPRING 2 (15-2) (0) | 2023.12.12 |
231211_SPRING 2 (14-1) (0) | 2023.12.11 |
231208_SPRING 2 (13-2) (0) | 2023.12.08 |
231208_SPRING 2 (13-1) (0) | 2023.12.08 |