--------------

참고프로젝트 jpa 메서드도 참고

https://github.com/codingspecialist/Springboot-Security-OAuth2.0-V2

 

codingspecialist/Springboot-Security-OAuth2.0-V2

Contribute to codingspecialist/Springboot-Security-OAuth2.0-V2 development by creating an account on GitHub.

github.com

 

oauth 세팅 유튜브

youtu.be/WACDTFroYNI

 

현재 프로젝트

https://github.com/jaybon1/springwork/tree/master/securityOAuthEx01

 

jaybon1/springwork

Contribute to jaybon1/springwork development by creating an account on GitHub.

github.com

---------

 

이클립스 내에서 의존성 추가하기

 

--

 

 

OAuth2 Client 선택 추가

---

 

기존에 있는 서비스를 이용하면 DB를 이용해야 하기 때문에 카카오나 네이버 인증을 이용하기 어렵다

스프링에는 OAuth 서비스가 따로 있다

 

스프링 부트에서 구글 페이스북을 표준으로 하는 이유는 리턴하는 정보가 규격화 되어 있기 때문

이외의 회사들은 정보규칙이 다르고 코드 토큰 등의 구성 방식이 다를 수 있다

 

OAuth 구동 순서

더보기
             (요청)           
사용자 <---------> 서버
             (로그인페이지)

서버에서 인증하는 것이 아니라 구글 페이스북에 먼저 인증을 요청

사용자 ---------->   구글 페이스북 ( 로그인 )

구글 페북에서 인증되면 서버에 코드를 준다

구글 페이스북 -------> 서버 (코드를 준다)

서버는 코드를 이용해서 구글 페이스북 액세스 토큰을 요청한다 
토큰을 받으면 (권한을 받은 것)

서버 -------> 구글 페이스북   (토큰요청)

구글 페이스북 -------> 서버 (토큰을 준다)

액세스 토큰으로 스코프를 요청 할 수 있다

서버 -------> 구글 페이스북   (스코프요청)

구글 페이스북 -------> 서버 (사용자 정보 스코프를 준다)

 

https://github.com/codingspecialist/Springboot-Oauth2.0-Facebook-Google-Login-Session

 

codingspecialist/Springboot-Oauth2.0-Facebook-Google-Login-Session

Contribute to codingspecialist/Springboot-Oauth2.0-Facebook-Google-Login-Session development by creating an account on GitHub.

github.com

 

약속된 규칙 - 라이브러리를 사용하면 주소를 아래와 같이 맟춰 줘야한다

/oauth2/authorization/google

/oauth2/authorization/ 주소를 OAuth-Client가 낚아채서 분석한다

분석하여 구글쪽으로 던져서 구글로그인 화면을 사용자에게 보내준다

 

yml 파일에 중요한 정보가 있을 경우 암호화파일을 만들어서 인증서로 이용
또는 파일을 다른 디렉토리에 만들어서 깃헙에 업로드 안되도록 하여도 된다
또는 깃에서 해당파일을 이그노어 처리한다

oauth + 세션 연동하는 것이 중점

yml 파일은 하위라면 2칸 띄우기, 값넣을 때는 1칸 띄우기

  security:
    oauth2:
      client:
        registration:
          google:
            client-id: 클라이언트키
            client-secret: 비밀키
            scope:
            - email
            - profile #스코프는 회사마다 다르기 때문에 적음

 

SecurityConfig.java

둘다 쓸거면 

.oauth2Login() 
.loginPage("/login")

를 적지말고

OAuth로그인만 쓸거면

.formLogin() 
.loginPage("/login")
.loginProcessingUrl("/loginProc")
.defaultSuccessUrl("/") 

를 안적으면 됨

 

.formLogin() 은 기본적으로 UserDetails 서비스를 탄다

반대로

.oauth2Login()은 직접 커스텀해줘야한다

 

패키지 추가

 

DefaultOAuth2UserService를 상속

 

무조건 오버라이드 하나해야함 loadUser

 

OAuth2User 오오쓰로그인 
UserDetails 일반로그인

두개의 인증세션이 생기는데 둘 중 하나만 쓸 것

 

PrincipalDetails.java

타입을 맟추기 위해 OAuth2User를 임플리먼트

 

 

 

 

oAuth2User를 리턴하는 게아니라 principalDetails를 리턴하면 세션을 하나로 관리 가능하다

 

테스트를 위해 new 해본다

 

 

내 구글 서버의 프라이머리키

sub= 프로바이더아이디
name= 풀네임
given_name= 이름, family_name= 성씨,
picture= 사진
email= 이메일
email_verified= 확인
locale= 지역

 

[ROLE_USER, SCOPE_https://www.googleapis.com/auth/userinfo.email, SCOPE_https://www.googleapis.com/auth/userinfo.profile, SCOPE_openid]



등등이 보인다

 

일반로그인은 
    provider:
    prividerId:
가 있는 사람은 못하게 막는다

User{
    id: 자동증가값
    username: 프로바이더_프로바이더 아이디
    password: 임의의 문자열을 해시값으로 만들어 입력
    emaill: 스코프의 이메일
    provider: 프로바이더
    prividerId: 프로바이더 아이디
}

 

 

 

userRequestClientRegistration{
registrationId='google', 
clientId='클라이언트아이디', 
clientSecret='클라이언트 시크릿',
clientAuthenticationMethod=org.springframework.security.oauth2.core.ClientAuthenticationMethod@592d42e,
authorizationGrantType=org.springframework.security.oauth2.core.AuthorizationGrantType@5da5e9f3, 
redirectUriTemplate='{baseUrl}/{action}/oauth2/code/{registrationId}', 
scopes=[email, profile], providerDetails=org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails@11e8362c, 
clientName='Google'}

{action}은 현재 login이다

 

이제 할 일은 OAuth2User와 PrincipalDetails를 합치는것 

 

 

 

리턴을 위에서 만든 함수로 변경

 

oAuth2User 는 Map이라서 관리하기 힘들다 그래서 클래스를 따로 만들어줌

 

구글 프로바이더 아이디 = sub

페이스북 프로바이더 아이디 = id

 

제공자(구글 페북)가 응답해주는 속성값이 달라서 공통으로 만들어준다

Oauth하나만 할거면 만들 필요 없다

PrincipalOauth2UserService.java에서만 사용할 클래스

 

 

 

SecurityConfig.java 수정

 

이 서비스에 접근해서 이용할 경우가 있기 때문에 싱글톤으로 잡아준다

 

 

 

 

 

userRequest - 코드 토큰 유저정보 등 모든 정보

oAuth2User - 토큰을 통해 응답받은 회원정보

 

 

UserRepository.java

쿼리는 연습삼아 만들어봄

 

@Service를 등록해야 SecurityConfig.java에 autowired가 되기 때문에 등록

userRepository를 @Autowired 해준다

 

 

 

 

 

 

 

빨간줄이 뜬다

 

Jpa 파일을 열어보면 옵셔널타입이다

제네릭 안의 오브젝트가 널이거나 널이 아니거나 담을 수 있다

즉 Optional 이 널을 감싸고 있기 때문에 널포인트 익셉션를 피할 수 있을 것

isPresent()는 값이 있는지 확인하여 불린
get()은 값을 반환
orElse() 값이 없으면 기본값 (빈객체) 반환 / 있으면 그대로 반환
orElseThrow() 값이 없으면 익셉션 발생 / 있으면 그대로 반환

 

첫번째 방법

.get()은 isPresent()이후에 꼭 쓰자

 

1번 검색

 

없는 번호 검색

 

------------

JPA에서는 옵셔널을 지향하자

----------

 

두번째 방법

new User()를 하든 원하는 값을 채우든 하면 된다

 

또는 화살표함수 (함수가 하나일 경우!)

 

 

 

 

X자리에 익셉션을 넣는다

 

 

또는 화살표 함수 (함수가 하나라서) + 어떤 익셉션이올지 모를경우

 

sb에 스크립트를 넣어서 이동시켜도됨

value에 값을 여러개 넣을 경우 중괄호로 묶자

 

---

 

비밀번호를 넣지 않음 (Oauth로그인)

 

 

일반로그인은 비밀번호가 암호화 되기 때문에 값을 넣지 않으면 로그인안되고

Oauth로그인은 비밀번호가 없더라도 알아서 로그인 된다

 

일반로그인시

 

Oauth 로그인시

 

 

PrincipalDetails.java

생성자 만들어줌

 

 

실행해보면

 

PrincipalDetails.java

이렇게 합쳐주지 않으면 세션이 2개가 저장된다

@AuthenticationPrincipal PrincipalDetails

 

 

 

구조

빨간박스 이외의 메뉴들은 지금 블로깅과 크게 관련이 없습니다.

 

코드설명

 

frag1.xml
frag2.xml

프래그먼트를 2개 만들어 봅니다
아래와 같이 간단히 구분할 수 있을 정도로만 만들어서
첫번째와 두번째 프래그먼트를 만듭니다 .

 

 

 

activity_main.xml

빨간박스 이외의 것은 신경쓰지 않으셔도 됩니다.
리니어 레이아웃으로 구성하여 탭레이아웃과 뷰페이저를 입력합니다.

 

 

 

Frag1
Frag2

프래그먼트는 자바파일도 만들어 줘야합니다.
Fragment를 상속받고
onCreateView를 오버라이딩하여
infalter를 이용하여 뷰를 리턴합니다.

기타설명은 주석 참조

 

 

 

FragmentAdapter

프래그먼트를 이용하려면 프래그먼트 어댑터를 쓰는 것이 좋습니다.
FragmentPagerAdapter 또는 FragmentStatePagerAdapter를 원하는대로 상속합니다.

fragmentList : 프래그먼트를 모두 담는 리스트
addFragment : 프래그먼트에 아이템 추가
getItem : 프래그먼트를 가져오기
getCount : 프래그먼트 개수 확인

 

 

 

MainActivity

TabLayout / ViewPager / FragmentAdapter 를 전역변수에 추가하고
onCreate에서 연결시켜준다

연결시켜줄 때
adapter = new FragmentAdapter(getSupportFragmentManager(),1); 의 주석 확인

addFragment로 어댑터에 프래그먼트를 추가하고

뷰페이저에 어댑터를 연결 + 탭레이아웃에 뷰페이저를 연결

마지막으로 탭레이아웃의 탭이름을 정해준다

 

 

 

결과

 

 

 

코드 확인

https://github.com/jaybon1/androidwork/tree/master/toolbarTest

 

jaybon1/androidwork

Contribute to jaybon1/androidwork development by creating an account on GitHub.

github.com

 

 

구조

Frag와 관련된 것은 신경쓰지 말고 나머지만 확인하자

 

코드 설명

 

build.gradle(Module: app)

머터리얼 라이브러리를 추가한다

 

 

 

styles.xml

기본 액션바를 없애고 툴바를 만들 것이기 때문에 
NoActionBar를 입력하자

 

 

 

toolbar.xml

툴바 역할을 할 페이지를 만든다

렐러티브 레이아웃을 사용하고 넓이는 매치 높이는 직접 조절하였음
텍스트뷰를 하나 만들어서 중앙에 배치하고
(종이 위에 작은 종이를 올리듯이) 이미지뷰를 텍스트뷰 아래에 작성하여 텍스트뷰 보다 위로 오도록 한 뒤
부모의 오른쪽에 붙도록 만들었음

 

 

 

nav_header.xml

툴바의 햄버거 버튼을 누르면 나오는 화면중 헤더를 만든다

레이아웃은 크게 상관 없을듯 하며
화면을 위아래로 채우기위해 height를 매치로 한듯 하다
텍스트 뷰를 이용하여 간단히 헤더를 만들어준다.

 

 

nav_menus.xml

메뉴를 누르면 나올 메뉴들을 작성한다

 

 

 

 

activity_main.xml

화면 위에 서랍처럼 메뉴가 밀려와야 되기 때문에
드로어레이아웃을 사용해준다

그리고 드로어레이아웃 하위에 네비게이션뷰를 만들어 주고
end|right를 이용하여 오른쪽에서 밀려나오도록 한다

또한 네비게이션 뷰 안에

        app:headerLayout="@layout/nav_header"
        app:menu="@menu/nav_menus"

위의 코드를 입력하여 헤더와 메뉴들이 나오도록한다

네비게이션뷰 하위에 툴바를 인클루드 시켜준다

 

 

MainActivity

햄버거버튼인 ImageView와
메인레이아웃인 DrawerLayout을 전역변수로설정하고
onCreate에서 xml id로 연결해준다

그리고 imageView에 클릭리스너를 달아줘서
누르면 옆에 있는 것들이 나오도록 한다.

 

 

 

결과

 

 

 

----

스프링 시큐리티 보안 공부

https://spring.io/guides/gs/securing-web/

 

Securing a Web Application

this guide is designed to get you productive as quickly as possible and using the latest Spring project releases and techniques as recommended by the Spring team

spring.io

------

세션

Security-Context (key) 밑에 방대한 값(value)들이 있다
그 중에
Authentication( 인증에 필요한 권한, 유효한 이용자인지 등 모든 필드가 정해져 있다 )
필드들이 정해져 있다는 것은 만들어서 넣어야 한다는 뜻
Authentication Manager의 도움을 받아서 Authentication을 만든다
Authentication의 필수 입력 값은 (username, password)
Authentication의 구성은 정해진 필드를 따라 만들어야 한다 ( User, UserDetails )
UserDetails ( 유저네임, 패스워드, 권한 )를 만들 때 컴포지션하거나
내 커스텀 유저정보 java 파일에 UserDetails을 extends 하면 된다
접근할 때 DI를 사용하면 된다

 

----------------

시큐리티

 

요약

1. 프로젝트 생성
2. Spring Security 디펜던시 추가
3. yml 파일 설정
4. DB 생성
5. 서버 가동 및 로그인 로그아웃 테스트 (비밀번호는 콘솔창의 해시값)
6. SecurityConfig파일 생성 및 @Configuration @EnableWebSecurity 추가
6-1. WebSecurityConfigurerAdapter 상속
6-2. @EnableGlobalMethodSecurity(prePostEnabled = true) 추가
6-3. configure(HttpSecurity http) 오버라이딩
6-4. http객체에 설정하기
6-5. 폼태그의 post 요청시 거부를 당하지 않기 위해 http.csrf().disable(); 추가

 

새 스프링부트 프로젝트 생성

 

 

파일명 그룹 패키지 설정

 

 

spring security / spring data JPA를 세팅해서 연습해본다

템플릿을 바로추가하려면 여기서 머스태치나 타임리프를 사용

 

 

참고사항 : com.jaybon.securityEx01 아래에 만들어야 컴포넌트 스캔이 된다

 

 

properties파일을 yml파일로 변경

 

 

포트 / 컨텍스트패스 / 캐릭터셋을 설정하고 enabled (인코딩 활성화) / force(인코딩 강제활성화)

server:
  port: 8080
  servlet:
    context-path: /
    encoding:
      charset: utf-8
      enabled: true
      force: true

 

 

 

 

jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Seoul

(security라는 데이터베이스에 접속한다는 뜻)

 

 

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Seoul
    username: cos
    password: cos1234

 

 

(mysql DB 생성 및 연결을 전체적으로 보려면 아래의 링크 확인)

https://blog.naver.com/getinthere/221708608489

 

JSP AWS 배포 하기 - MySQL + Tomcat

https://youtu.be/BH7De5EEoU4​만약에 AWS에 배포한 프로젝트 내부의 META-INF의 context...

blog.naver.com

 

 

유저와 데이터

create user 'cos'@'%' identified by 'cos1234';
GRANT ALL PRIVILEGES ON *.* TO cos@'%';
create database security;
use security;

 

 

 

실행 해보면 잠겨있다

루트 컨텍스트로 이동했음에도 불구하고 로그인페이지로 강제 이동한다

인터셉터에서 가로채서 막은 것이다

(각종 공격을 다 막아 준다)

 

 

필터 - 디스패쳐 - 인터셉터 - 컨트롤러

 

 

로그에 찍힌 패스워드를 입력

비밀번호는 (해시)로 되어 있다

 

 

 

 

로그아웃 하려면

http://localhost:8080/logout

 

 

 

----------

위의 모양이 맘에 안든다면 로그인 페이지 회원가입 페이지를 커스텀 할 것

-----------

 

config 패키지 생성

 

 

설정파일 하나 생성

 

필터 - 디스패쳐 - 인터셉터 - 컨트롤러

 

필터와 디스패쳐 인터셉터 사이에

시큐리티 필터 체인 ( 많은 필터가 모여있다, xss, 주소, ip, 인증 등) - 체인이 있다는 것을 기억

필터를 낚아채려면 오버라이딩 또는 extends로 재정의

 

 

전체 필터를 관리할 수 있는 클래스 (@EnableWebSecurity)

어댑터가 있다는 것은 일부 원하는 것만 오버라이딩 할 수 있다는 것

 

 

@EnableGlobalMethodSecurity(prePostEnabled = true) 를 추가하여 컨트롤러 접근 전에 낚아채기

 

 

http요청을 제어하는 함수 추가

 

 

루트컨텍스트(/)로 가는 것은 모두 허용하도록 설정

 

 

로그인 없이 바로 들어가진다

 

 

controller 패키지 추가

 

 

연습용 라우팅들 추가

 

 

다른페이지로 이동하면 권한이 없어서 페이지가 뜨지 않는다(403)

 

 

파즈티브 방식(열것만 열고 나머지는 다 잠그기)

 

네거티브 방식(다열고 잠글것만 잠그기) - 우리가 만들 방식

loginPage를 이용하여 인증이 필요한 상황이면 /login으로 리다이렉트한다

 

 

http://localhost:8080/admin -> http://localhost:8080/login

(파일이 없어서 404)

 

 

로그인을 리턴해주는 라우팅을 만들면

 

 

 

 

머스태치 

 

메이븐 리파지토리에서 spring mustache 검색

 

 

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-mustache</artifactId>
		</dependency>

 

 

추가해주고 서버끄고 메이븐리파지토리 업데이트 무조건!

 

 

만약 템플레이트가 적용이 안된다면 yml 파일에 아래코드 추가 (안해도 작동되지만 혹시 안된다면 사용)

  mvc:
    view:
      prefix: /templates/
      suffix: .mustache

 

 

로그인 페이지에서 로그인 요청이 오면 loginProc

 

 

템플릿에서 name을 잘 적어줘야한다

 

 

loginProc는 컨트롤러에서 낚아채는 것이 아니라 loginProcessingUrl에서 낚아채서 수행

(formLogin이라면 템플릿에서 form타입으로 날려야한다)

 

 

성공하면 해당주소로 이동 defaultSuccessUrl (Ajax와 같이 쓰지말 것 : 응답이 두번되기 때문에 꼬임)

로그인만! Ajax 쓰지말자 (회원가입은 써도됨)

 

 

@RestController를 Controller로 변경하고 데이터를 리턴하려면 @ResponseBody 추가

 

 

컨트롤러에 회원가입 라우팅 추가

 

 

템플릿에 회원가입 추가 (회원가입은 ajax로 바꾸어도 무방)

 

 

 

컨트롤러에 회원가입 프로세스 라우팅 추가

(SecurityConfig에서 ProcessingUrl을 안 만들었기 때문)

 

 

@RequestBody는 제이슨(또는 데이터) 일때만 사용

 

 

model 패키지 추가

 

 

유저모델추가

 

 

입력 테스트

 

 

포비든이 뜬다 무엇이 문제일까

 

 

POST일때만 폼태그로 요청 토큰(csrf-token)이 필요하다!

(csrf = 해커가 임의대로 요청을 해서 공격)

csrf-token을 임시로 만들어줘서 줘야한다

(스프링에서 csrf 토큰 만들기 검색 공부)

 

방법(1. csrf토큰 비활성화 // 2. 토큰 만들기)

 

현대 프로그래밍은 자바스크립트로 하기 때문에 csrf를 잘 당하지 않는다 (수업에선 비활성화)

SecurityConfig.java

 

 

회원가입을 하면 인덱스페이지로 이동된다

 

 

-------

 

JPA

연습 용이라 서비스 안만들고 레파지토리로 끝냄

 

JPA와 Spring Data JPA

https://suhwan.dev/2019/02/24/jpa-vs-hibernate-vs-spring-data-jpa/

 

JPA, Hibernate, 그리고 Spring Data JPA의 차이점

개요 Spring 프레임워크는 어플리케이션을 개발할 때 필요한 수많은 강력하고 편리한 기능을 제공해준다. 하지만 많은 기술이 존재하는 만큼 Spring 프레임워크를 처음 사용하는 사람이 Spring 프레�

suhwan.dev

 

 

모델에서 @Entity를 추가(데이터베이스의 모델)

 

 

IDENTITY로 설정 (오라클은 SEQUENCE를 사용, IDENTITY는 해당 DB특유의 전략 사용)

 

 

 

자바모델 만들어 질 때 테이블 생성

자바모델 그대로 데이터베이스에 테이블이 생성된다

(생성 create 업뎃만 update 아무것도안하려면 none)

update 만 선택해도 테이블생성까지 가능하다!

 

 

mysql에서 셀렉트해보면 만들지도 않은 테이블이 생성되어 있다

 

 

모델에서 createDate를 만들고 저장만 하면!

@CreationTimestamp 세팅 (시간 자동입력)

(java.security -> java.sql 로 만들어야한다)

 

 

DB에서 속성이 자동으로 생성되어있다

 

 

 

 

회원가입 해보면

 

 

서버를 켜보면 하이버네이트 Hibernate 

(서버를 켤때 마다 지워지고 다시 만들어진다)

디폴트는 언더바 전략

 

네이밍 전략

 

 

커멜 표기법 전략으로 변경

  jpa:
    hibernate:
      ddl-auto: update #create update none 3개 중 하나 선택
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    show-sql: true

 

 

레파지토리

jpa는 어노테이션이나 컨피그 데이터 소스가 아니라 JpaRepository를 상속

제네릭 첫번째 인자는 모델클래스, 두번째 인자는 모델의 프라이머리 키의 타입을 의미한다

 

 

컨트롤러에서 DI

 

 

한줄추가

 

 

입력해보면 들어가있다

(로그인시 오류가 날것이다 암호화가 되어있지않음)

 

BCryptPasswordEncoder (안좋은방법 다른방법 쓸 것)

encode

 

 

SecurityConfig.java

 

 

IndexController.java

 

 

bCryptPasswordEncoder를 이용해 해시화 시켜줌

 

 

로그인

10. Authentication 객체 생성

Authentication은 AuthenticationFilter의 자식

AuthenticationFilter는 loginProc가 호출될 때 작동

UsernamePasswordAuthenticationToken 토큰이 있어야 로그인됨

 

https://getinthere.tistory.com/29

 

스프링부트 with JPA 블로그 13강 - 시큐리티 동작 원리

1. 스프링 mvc request life cycle 2. Filter와 Interceptor는 실행 시점이 다르다. Filter는 WebApplication에 등록 - web.xml Interceptor는 Spring의 Context에 등록 필터체인 예제와 인터셉터 예제하기 3. In..

getinthere.tistory.com

 

 

1아이디패스워드받아서

2토큰을받고

3유저디테일서비스가 받음

4유저디테일서비스는 유저디테일즈를 리턴

5어썬티케이션매니저가 받아서 어썬티케이션에 넣는다

6유저디테일은 시큐리티에 꼭 필요한 필드들이 있기 때문에 무조건 유저디테일로

7내가 만든 유저 모델을 유저디테일 타입으로 바꿔줘야한다

8어썬티케이션은 세션에 담긴다
(Map<String, Authentication> 객체이름 = new HashMap<>();)
(aa.put("Security-Context", auth))
(session.setAttribute("Security-Context-Holder", 해시맵))
쉽게 접근 못하도록 싸매져 있다

AuthenticationManager를 이용해서 Authentication를 저장할 수 있다(강제로 저장)

 

 

패키지 생성

 

 

유저디테일즈를 리턴해주기 위해서 클래스 하나 생성

모델 유저에 유저디테일즈를 상속받으면 유저모델이 망가지기 때문에 새로만들어줌

 

 

UserDetails를 임플리먼트해주고 오버라이딩

Authentication 객체에 저장할 수 있는 유일한 타입 - UserDetails

시큐리티 입장에서 관리해줄 수 있게 정해져 있다

 

 

User 모델 콤포지션

더보기
package com.jaybon.securityEx01.config.auth;

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.jaybon.securityEx01.model.User;

import lombok.Data;


//Authentication 객체에 저장할 수 있는 유일한 타입
@Data
public class PrincipalDetails implements UserDetails{
	
	private User user;
	
	public PrincipalDetails(User user) {
		super();
		this.user = user;
	}
	
	

	@Override // 사용자의 비밀번호를 알고 싶으면 호출
	public String getPassword() {
		return user.getPassword();
	}

	@Override // 사용자의 유저네임를 알고 싶으면 호출
	public String getUsername() {
		return user.getUsername();
	}

	@Override // 사용자가 만료된 지를 알고 싶으면 호출
	public boolean isAccountNonExpired() { // 만료안됐니?
		//접속시간확인하여 true false 리턴
		return true;
	}

	@Override 
	public boolean isAccountNonLocked() { // 락 안걸렸니?
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isEnabled() { // 계정활성화 되어있니?
		return true;
	}
	
	@Override // 어떤 권한을 가졌니?
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO Auto-generated method stub
		return null;
	}

}

 

 

 

 

UserDetailsService를 만들어야함

 

 

JPA 쿼리 짜는법

 

jpa 쿼리 생성룰

https://papababo.tistory.com/272

 

[Spring Data Rest] Query creation - 쿼리 생성 룰

Spring Data Rest 사용시, 또는 JPA 기본 검색기능활용시, 아래의 네이밍 룰 따르면 끝! https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation Table 4. Suppo..

papababo.tistory.com

 

 

로그인 해보면 성공

 

-------

권한리턴하기

유저 권한 리턴하기 (주석은 Arrays)

스프링 내부에서 원하는 GrantedAuthority 타입으로만 설정해서 리스트에 저장하여 리턴

----------

 

구조

 

PrincipalDetails.java

더보기
package com.jaybon.securityEx01.config.auth;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.jaybon.securityEx01.model.User;

import lombok.Data;


//Authentication 객체에 저장할 수 있는 유일한 타입
@Data
public class PrincipalDetails implements UserDetails{
	
	private User user;
	
	public PrincipalDetails(User user) {
		super();
		this.user = user;
	}
	
	

	@Override // 사용자의 비밀번호를 알고 싶으면 호출
	public String getPassword() {
		return user.getPassword();
	}

	@Override // 사용자의 유저네임를 알고 싶으면 호출
	public String getUsername() {
		return user.getUsername();
	}

	@Override // 사용자가 만료된 지를 알고 싶으면 호출
	public boolean isAccountNonExpired() { // 만료안됐니?
		//접속시간확인하여 true false 리턴
		return true;
	}

	@Override 
	public boolean isAccountNonLocked() { // 락 안걸렸니?
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isEnabled() { // 계정활성화 되어있니?
		return true;
	}
	
	// Arrays.asList(new SimpleGrantedAuthority(user.getRole()));
	@Override // 어떤 권한을 가졌니?
	public Collection<? extends GrantedAuthority> getAuthorities() {
		
		Collection<GrantedAuthority> authList = new ArrayList<>();
		authList.add(new SimpleGrantedAuthority(user.getRole()));
		return authList;
		
	}

}

PrincipalDetailsService.java

더보기
package com.jaybon.securityEx01.config.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.jaybon.securityEx01.model.User;
import com.jaybon.securityEx01.repository.UserRepository;

// UserDetailsService는  IoC로 찾음

@Service // UserDetailsService타입으로 메모리에 뜬다 (덮어씌워짐)
public class PrincipalDetailsService implements UserDetailsService{
	
	@Autowired
	private UserRepository userRepository;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// 어썬티케이션 매니저가 낚아챔
		// JPA는 기본적인 CRUD만 있어서 다른걸 쓰려면 만들어줘야함 
		
		User user = userRepository.findByUsername(username);
		
		if(user == null) {
			return null;
		}
		
		return new PrincipalDetails(user);
	}

}

SecurityConfig.java

더보기
package com.jaybon.securityEx01.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration // IoC 빈(bean, 인스턴스)을 등록
@EnableWebSecurity // 필터 체인 관리 시작 어노테이션
@EnableGlobalMethodSecurity(prePostEnabled = true) // 컨트롤러 접근 전에 낚아챔, 특정 주소 접근시 권한 및 인증 미리체크
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
	@Bean // IoC에 등록되어 컨피그가 호출될 때 생성, 메서드를 IoC하는 방법
	public BCryptPasswordEncoder enc() { // 마땅히 둘 곳이 없어서 둔 것 Controller를 제외한 곳에 둠
		return new BCryptPasswordEncoder();
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		http.csrf().disable(); // csrf 비활성화
		
		http.authorizeRequests()
			.antMatchers("/user/**", "/admin**") 
			.authenticated()
			.anyRequest()
			.permitAll()
			.and()
			.formLogin()
			.loginPage("/login") // 인증이 필요한 곳에 /login 으로 리다이렉트
			.loginProcessingUrl("/loginProc") // 필터체인에서 인지하고 있다가 시큐리티가 낚아채서 Authentication Manager
			.defaultSuccessUrl("/"); // 성공하면 해당 주소로 이동 / 슬래시만 달면 이전 주소로 이동
	}
}

IndexController.java

더보기
package com.jaybon.securityEx01.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.jaybon.securityEx01.config.auth.PrincipalDetails;
import com.jaybon.securityEx01.model.User;
import com.jaybon.securityEx01.repository.UserRepository;

@Controller
public class IndexController {
	
	@Autowired
	private UserRepository userRepository;
	
	@Autowired
	private BCryptPasswordEncoder bCryptPasswordEncoder;

	@GetMapping({ "", "/" })
	public @ResponseBody String index() {
		return "인덱스 페이지입니다";
	}

	@GetMapping("/user")
	public @ResponseBody String user(@AuthenticationPrincipal PrincipalDetails principalDetails) {
		System.out.println("확인"+principalDetails);
		System.out.println(principalDetails.getUser().getRole());
		System.out.println(principalDetails.getAuthorities()); // 출력 했을 때 사용자의 모든 권한을 리턴
		return "유저 페이지입니다";
	}

	@GetMapping("/admin")
	public @ResponseBody String admin() {
		return "어드민 페이지입니다";
	}

	@GetMapping("/login")
	public String login() {
		return "login"; // 머스태치를 pom에 추가했으니 서픽스는 templates 프리픽스는 .mustatche
	}

	@GetMapping("/join")
	public String join() {
		return "join";
	}

	@PostMapping("/joinProc")
	public String joinProc(User user) {
		System.out.println("회원가입 진행" + user);
		String rawPassword = user.getPassword();
		String encPassword = bCryptPasswordEncoder.encode(rawPassword);
		user.setPassword(encPassword);
		user.setRole("ROLE_USER");
		userRepository.save(user);
		return "redirect:/";
	}

}

User.java

더보기
package com.jaybon.securityEx01.model;

import java.sql.Timestamp;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import org.hibernate.annotations.CreationTimestamp;

import lombok.Data;

// ORM - Object Relation Mapping - 알아서 데이터베이스 테이블 만들어 준다

@Data
@Entity // 이것을 토대로 데이터베이스 모델을 만들 수 있다.
public class User {
	@Id // primary key를 걸어주는 어노테이션
	@GeneratedValue(strategy = GenerationType.IDENTITY) // 오라클은 시퀀스 전략, mysql은 오토인크리먼트 전략
	private int id;
	private String username;
	private String password;
	private String email;
	private String role; // ROLE_USER / ROLE_ADMIN
	@CreationTimestamp
	private Timestamp createDate;
}

UserRepository.java

더보기
package com.jaybon.securityEx01.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.jaybon.securityEx01.model.User;

// JpaRepository를 상속하면 자동 스캔됨.
public interface UserRepository extends JpaRepository<User, Integer>{

	//Jpa Naming 전략
	// SELECT * FROM user WHERE username = ?
	User findByUsername(String username); // 함수이름에 맞게 쿼리가 동작한다
	
//	// SELECT * FROM user WHERE username = ? AND password = ?
//	User findByUsernameAndPassword(String username, String password);
//	
//	@Query(value = "SELECT * FROM user", nativeQuery = true)
//	User 내맘대로();
	
}

join.mustache

더보기
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입 페이지</title>
</head>
<body>
	<h1>회원가입 페이지</h1>
	<hr/>
	<form action="/joinProc" method="post">
		<input type="text" name="username" placeholder="username">
		<input type="password" name="password" placeholder="password">
		<input type="email" name="email" placeholder="email">
		<button>회원가입</button>
	</form>
</body>
</html>

login.mustache

더보기
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인 페이지</title>
</head>
<body>
	<h1>로그인 페이지</h1>
	<hr/>
	<!-- 시큐리티는 x-www-form-urlencoded 타입만 인식 -->
	<form action="/loginProc" method="post">
		<input type="text" name="username">
		<input type="password" name="password">
		<button>로그인</button>
	</form>
</body>
</html>

application.yml

더보기
server:
  port: 8080
  servlet:
    context-path: /
    encoding:
      charset: utf-8
      enabled: true
      force: true
      
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Seoul
    username: cos
    password: cos1234
    
#  mvc:
#    view:
#      prefix: /templates/
#      suffix: .mustache


  jpa:
    hibernate:
      ddl-auto: update #create update none 3개 중 하나 선택
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    show-sql: true
      
      
      

 

 

 

 

리플렉션

스프링 구동 -> 스프링 컨텍스트가 램에 할당 -> 

(스프링 컨텍스트)

 

-> 어노테이션 스캔( 컴포넌트 스캔  )

(스캔 하는 위치)

스캔 하는 것을 리플렉션이라고 한다

---

리플렉션 - 실행 타임에 메모리를 찾아서 IOC된 객체를 찾는다
(ex 컨트롤러 실행시 IOC객체 찾기)

--------------

 

톰캣 실행 -> 스프링 실행

 

필터

1차 필터 - 톰캣이 가지고 있는 web.xml

2차 필터 - 스프링의 인터셉터

 

필터 또는 인터셉터를 설정 한다는 것은

기본으로 사용하는 필터를 disable하거나

기본으로 사용하지 않는 필터를 enable하거나

내가 직접 필터를 커스텀 하는 것

 

톰캣의 필터와 스프링의 인터셉터가 합쳐 쓰는 것을 시큐리티 라고 한다.

추후 webMvcConfigurer 가 다른것으로 바뀐다면 인터셉터에 접근하는 방법을 찾아야한다
(WebFlux 등)

인터셉터 + 필터 = 시큐리티

 

AOP =관점 지향 프로그래밍 - 함수의 직전과 직후를 처리할 수 있다

회원가입
직전 - 유효성체크
본문 - 회원가입
직후 - 세션등록 및 로그인처리

직전과 직후의 부가적인 기능을 밖으로 빼고 본문의 기능(관심 로직)을 작성

부가적인 기능은 [공통적]으로 쓸 수 있게 작성해놓고 사용 !! -> 핵심

-------

레거시 사용시

web.xml ------- 톰캣 시작시 발동

root-context.xml  -------- 메모리에 딱 1번만 올릴 것들 (DB관련 등) // 톰캣 시작시 발동
객체의 생성 및 관리


servlet-context.xml -------- 요청 올 때마다 메모리에 띄울 것들 // Request할 때 발동된다

----------

aop는 요청시마다 생겨야하니까 servlet-context.xml

----------

 

인터셉터 요약

1. @Configuration으로 설정파일 지정
2. 설정파일에 WebMvcConfigurer을 implements
3. 인터셉터만 구성하기위해 addInterceptors() 오버라이딩
4. registry에 인터셉터 추가 / 주소패턴 추가
5-1. 인터셉터 return true( 컨트롤러 함수진입 )
5-2. 인터셉터 return false( throw new Exception() )
6. @ControllerAdvice를 어노테이션한 익셉션 핸들러 진입
7. @ExceptionHandler(value = 익셉션이름.class)인 함수 실행

 

인터셉터용 자바파일 생성

 

 

 

 

인터셉터를 사용하기위해서 WebMvcConfigurer를 임플리먼트 한다

 

 

설정파일에는 @Configuration 을 추가

addInterceptors 인터셉터를 추가하면 WebConfig 클래스는 인터셉터의 역할을 하게 된다.

WebMvcConfigurer.super.addInterceptors(registry); 를 삭제한다.

 

 

(Dispatcher Servlet에서 스프링으로 보낼때 사이에서 핸들러가 중간에 낄 수 있다.)

핸들러 - 주소매핑( 핸들러 매핑 -> 디스패쳐서블릿 -> 핸들러 어댑터 -> 인터셉터(?) -> 컨트롤러 )

 

 

프리핸들러로 컨트롤러의 함수에 진입하기 직전을 컨트롤 할 수 있다.

아래는 HandlerInterceptor 인터페이스를 new 해서 preHandle을 오버라이딩 한 상태

 

 

HandlerInterceptor 인터셉터를 커스텀해주자

 

HandlerInterceptorAdapter를 상속받아서 인터셉터 클래스를 만든다

 

 

 

WebConfig.java에 위에서 커스텀 해준 인터셉터들을 넣고

PathPatterns를 각각 설정해준다

 

 

실행해서 /user 하위경로를 request하면 자동으로 인터셉트한다.(세션을 체크해야함 메시지 확인)

 

 

커스텀한 인터셉터에서 세션 등을 확인하여 

문제가 있을시 컨트롤러 함수로 진입하지 않고 메세지를 띄운 뒤 다른 페이지로 리다이렉션 시킨다

문제가 없을시 컨트롤러 함수로 이동

 

 

로그인 하지 않고 진입시 화면

 

 

로그인 후 진입시 접속된다

 

 

위와 마찬가지로 RoleIntercepter도 세팅

( 스샷의 오류 : principal.getRole() 앞에 !를 넣어야한다)

 

 

 

로그인을 해도 어드민이 아니라면 익셉션

 

 

어드민으로 로그인해서 접속하면 관리자페이지로 접속

 

 

위와 같이 컨트롤 해도 되나 익셉션을 통한 핸들링도 가능하다

핸들러 패키지 추가

 

 

(주석과 같이 익셉션별로 구분해야하나 지금은 Exception을 넣음)

 

 

익셉션을 throw 하면 이 파일로 연결되어 리턴

@ControllerAdvice를 어노테이션하면 throw 한 익셉션이 이 파일로 이동된다

그리고 @ExceptionHandler에 맞는 익셉션을 찾아서 함수로 진입한다

 

 

throw new Exception() 을 이용하여 익셉션을 개발자가 낚아채도록 한다

 

 

익셉션을 통한 권한체크

 

 

익셉션을 커스텀 하기 위해 파일 생성

 

 

Exception을 상속받는다

 

 

커스텀한 익셉션을 입력한다

 

 

익셉션 핸들러에도 커스텀한 익셉션을 적용한다

 

 

IO익셉션으로 설정해놓으면 터진다 ( 함수가 없기 때문(?))

 

 

 

----------

로그기록 api

https://sentry.io/welcome/

 

Application Monitoring and Error Tracking Software

Self-hosted and cloud-based error monitoring that helps software teams discover, triage, and prioritize errors in real-time.

sentry.io

-----------

 

https://blog.naver.com/getinthere/221718319587

 

springboot 8강 - Spring 인터셉터

인터셉터란 PPT아래와 같이 두 가지만 파일만 정의해주면 된다.config/WebMvcConfig.javaconfig/Ses...

blog.naver.com

 

핸들러는 인터셉터다

 

Spring+HandlerInterceptor.pptx
0.64MB

 

 

 

---

컨트롤러 - 뷰리졸버 발동 (@ResponseBody쓰면 미발동)

Rest컨트롤러 - 뷰리졸버 미발동

------

스프링 리퀘스트 라이프 사이클

-------------

 

 

Dispatcher Servlet 에서 FrontController 타고 Intercepter가 있으면 탄다

 

 

 

 

 

 

postHandle도 이용할 수 있으며 modelAndView 라는 객체도 가지고 있다

modelAndView 는 JSP에서의 모델이다
response 시 같이 가져가게된다

 

 

프리핸들러 - 세션 등을 확인

 

 

 

본문내용은 여기 까지 입니다.

-------------------------------------------

로그아웃

 

redirect: 를 사용하면 파일이 아닌 주소를 검색한다

 

-----------

 

글쓰기

 

세션 인터셉터에 등록

 

 

 

---------------------

디폴트로 만들어진 인터페이스는 구체화하지 않아도 오류가 나지 않는다

----------------

Log4J Logger

-------------

 

-----

마샬링

------

세션서버

리액트 있는 조는 이렇게 만들면 안됨 - jwt 세팅해야함

템플릿을 이용하는 조는 세션서버를 만들어야 한다 - csrf-token 비활성화 등 세팅

----

옵저버 패턴이 적용 되어 있는 리액티브 스프링

----

스프링 시큐리티 보안 공부

https://spring.io/guides/gs/securing-web/

 

Securing a Web Application

this guide is designed to get you productive as quickly as possible and using the latest Spring project releases and techniques as recommended by the Spring team

spring.io

------

 

 

 

 

 

 

spring security / spring data JPA 로 세팅

 

 

com.jaybon.securityEx01 아래에 만들어야 컴포넌트 스캔이 된다

 

 

DataSource (DB 풀링 해주는 객체)

 

 

yml파일로 변경

 

 

포트와 컨텍스트패스 캐릭터셋을 설정하고 enable force

server:
  port: 8080
  servlet:
    context-path: /
    encoding:
      charset: utf-8
      enabled: true
      force: true

 

 

 

 

jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Seoul

(security라는 데이터베이스에 접속한다는 뜻)

 

 

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Seoul
    username: cos
    password: cos1234

 

 

(mysql db연결)

https://blog.naver.com/getinthere/221708608489

 

JSP AWS 배포 하기 - MySQL + Tomcat

https://youtu.be/BH7De5EEoU4​만약에 AWS에 배포한 프로젝트 내부의 META-INF의 context...

blog.naver.com

 

 

 

 

실행 해보면 잠겨있다

인터셉터에서 막힌 것

각종 공격을 다 막아 준다

 

 

필터 - 디스패쳐 - 인터셉터 - 컨트롤러

 

 

로그에 찍힌 패스워드를 입력

비밀번호는 (해시)로 되어 있다

 

 

 

 

세션

Security-Context (key) 밑에 방대한 값(value)들이 있다

그 중에

Authentication( 인증에 필요한 권한, 유효한 이용자인지 등 모든 필드가 정해져 있다 )

필드들이 정해져 있다는 것은 만들어서 넣어야 한다는 뜻

Authentication Manager의 도움을 받아서 Authentication을 만든다

Authentication의 필수 입력 값은 (username, password)

Authentication의 구성은 정해진 필드를 따라 만들어야 한다 ( User, UserDetails )

UserDetails ( 유저네임, 패스워드, 권한 )를 만들 때 컴포지션하거나

내 커스텀 유저정보 java 파일에 UserDetails을 extends 하면 된다

접근할 때 DI를 사용하면 된다

 

 

로그아웃 하려면

http://localhost:8080/logout

 

 

----------

로그인 페이지 회원가입 페이지를 커스텀 할 것

 

 

설정파일 하나 생성

 

필터 - 디스패쳐 - 인터셉터 - 컨트롤러

 

필터와 디스패쳐 인터셉터 사이에

시큐리티 필터 체인 ( 많은 필터가 모여있다, xss, 주소, ip, 인증 등) - 체인이 있다는 것을 기억

필터를 낚아채려면 오버라이딩 또는 extends로 재정의

 

 

전체 필터를 관리할 수 있는 클래스 (@EnableWebSecurity)

 

어댑터가 있다는 것은 일부 원하는 것만 오버라이딩 할 수 있다는 것

 

 

 

 

http요청을 제어

 

 

 

 

로그인 없이 바로 들어가진다

 

 

 

 

 

 

다른페이지로 이동하면 권한이 없어서 페이지가 뜨지 않는다(403)

 

 

위의 방식이 파즈티브 방식(열것만 열고 나머지는 다 잠그기)이고 아래는 네거티브 방식(다열고 잠글것만 잠그기)

 

 

 

 

http://localhost:8080/admin -> http://localhost:8080/login

(파일이 없어서 404)

 

 

로그인을 리턴해주는 라우팅을 만들면

 

 

 

 

머스태치 

 

메이븐 리파지토리에서 spring mustache 검색

 

 

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-mustache</artifactId>
		</dependency>

 

 

추가해주고 서버끄고 메이븐리파지토리 업데이트 무조건!

 

 

또는 yml 파일에

  mvc:
    view:
      prefix: /templates/
      suffix: .mustache

 

 

 

 

템플릿에서 name을 잘 적어줘야한다

 

 

loginProc는 컨트롤러에서 낚아채는 것이 아니라 loginProcessingUrl에서 낚아채서 수행

(formLogin이라면 템플릿에서 form타입으로 날려야한다)

 

 

성공하면 해당주소로 이동 defaultSuccessUrl (Ajax와 같이 쓰지말 것 : 응답이 두번되기 때문에 꼬임)

로그인만! Ajax 쓰지말자 (회원가입은 써도됨)

 

 

컨트롤러에 회원가입 라우팅 추가

 

 

템플릿에 회원가입 추가 (회원가입은 ajax로 바꾸어도 무방)

 

 

 

 

컨트롤러에 회원가입 프로세스 라우팅 추가

(시큐리티 컨피그를 안만들었기 때문)

 

 

@RequestBody는 제이슨 일때만!

 

 

유저모델추가

 

 

입력 테스트

 

 

포비든이 뜬다 무엇이 문제일까

 

 

POST일때만 폼태그로 요청 토큰(csrf-token)이 필요하다!

(csrf = 해커가 임의대로 요청을 해서 공격)

csrf-token을 임시로 만들어줘서 줘야한다

(스프링에서 csrf 토큰 만들기 검색 공부)

 

방법(1. csrf토큰 비활성화 // 2. 토큰 만들기)

 

현대 프로그래밍은 자바스크립트로 하기 때문에 csrf를 잘 당하지 않는다 (수업에선 비활성화)

SecurityConfig.java

 

 

회원가입을 하면 인덱스페이지로 이동된다

 

 

-------

 

JPA

연습 용이라 서비스 안만들고 레파지토리로 끝냄

 

 

모델에서 @Entity를 추가(데이터베이스의 모델)

 

 

IDENTITY로 설정

 

 

 

 

모델 만들어 질 때 테이블 생성

(생성 create 업뎃만 update 아무것도안하려면 none)

 

 

mysql에서 셀렉트해보면 만들지도 않은 테이블이 생성되어 있다

 

 

모델에서 createDate를 만들고 저장만 하면!

@CreationTimestamp 세팅 (시간 자동입력)

 

 

DB에서 속성이 자동으로 생성되어있다

 

 

 

 

회원가입 해보면

 

 

서버를 켜보면 하이버네이트 Hibernate 

(서버를 켤때 마다 지워지고 다시 만들어진다)

디폴트는 언더바 전략

 

네이밍 전략

 

 

커멜 표기법 전략으로 변경

  jpa:
    hibernate:
      ddl-auto: update #create update none 3개 중 하나 선택
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    show-sql: true

 

 

레파지토리

jpa는 어노테이션이나 컨피그 데이터 소스가 아니라 JpaRepository를 상속

 

 

컨트롤러에서 DI

 

 

한줄추가

 

 

입력해보면 들어가있다

(로그인시 오류가 날것이다 암호화가 되어있지않음)

 

BCryptPasswordEncoder (안좋은방법 다른방법 쓸 것)

encode

 

 

SecurityConfig.java

 

 

IndexController.java

 

 

bCryptPasswordEncoder를 이용해 해시화 시켜줌

 

 

로그인

10. Authentication 객체 생성

Authentication은 AuthenticationFilter의 자식

AuthenticationFilter는 loginProc가 호출될 때 작동

UsernamePasswordAuthenticationToken 토큰이 있어야 로그인됨

 

https://getinthere.tistory.com/29

 

스프링부트 with JPA 블로그 13강 - 시큐리티 동작 원리

1. 스프링 mvc request life cycle 2. Filter와 Interceptor는 실행 시점이 다르다. Filter는 WebApplication에 등록 - web.xml Interceptor는 Spring의 Context에 등록 필터체인 예제와 인터셉터 예제하기 3. In..

getinthere.tistory.com

 

 

1아이디패스워드받아서

2토큰을받고

3유저디테일서비스가 받음

4유저디테일서비스는 유저디테일즈를 리턴

5어썬티케이션매니저가 받아서 어썬티케이션에 넣는다

6유저디테일은 시큐리티에 꼭 필요한 필드들이 있기 때문에 무조건 유저디테일로

7내가 만든 유저 모델을 유저디테일 타입으로 바꿔줘야한다

8어썬티케이션은 세션에 담긴다
(Map<String, Authentication> 객체이름 = new HashMap<>();)
(aa.put("Security-Context", auth))
(session.setAttribute("Security-Context-Holder", 해시맵))
쉽게 접근 못하도록 싸매져 있다

AuthenticationManager를 이용해서 Authentication를 저장할 수 있다(강제로 저장)

 

 

패키지 생성

 

 

유저디테일즈를 리턴해주기 위해서 클래스 하나 생성

모델 유저에 유저디테일즈를 상속받으면 유저모델이 망가지기 때문에 새로만들어줌

 

 

UserDetails를 임플리먼트해주고 오버라이딩

Authentication 객체에 저장할 수 있는 유일한 타입 - UserDetails

시큐리티 입장에서 관리해줄 수 있게 정해져 있다

 

 

User 모델 콤포지션

더보기
package com.jaybon.securityEx01.config.auth;

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.jaybon.securityEx01.model.User;

import lombok.Data;


//Authentication 객체에 저장할 수 있는 유일한 타입
@Data
public class PrincipalDetails implements UserDetails{
	
	private User user;
	
	public PrincipalDetails(User user) {
		super();
		this.user = user;
	}
	
	

	@Override // 사용자의 비밀번호를 알고 싶으면 호출
	public String getPassword() {
		return user.getPassword();
	}

	@Override // 사용자의 유저네임를 알고 싶으면 호출
	public String getUsername() {
		return user.getUsername();
	}

	@Override // 사용자가 만료된 지를 알고 싶으면 호출
	public boolean isAccountNonExpired() { // 만료안됐니?
		//접속시간확인하여 true false 리턴
		return true;
	}

	@Override 
	public boolean isAccountNonLocked() { // 락 안걸렸니?
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isEnabled() { // 계정활성화 되어있니?
		return true;
	}
	
	@Override // 어떤 권한을 가졌니?
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO Auto-generated method stub
		return null;
	}

}

 

 

 

 

UserDetailsService를 만들어야함

 

 

JPA 쿼리 짜는법

 

jpa 쿼리 생성룰

https://papababo.tistory.com/272

 

[Spring Data Rest] Query creation - 쿼리 생성 룰

Spring Data Rest 사용시, 또는 JPA 기본 검색기능활용시, 아래의 네이밍 룰 따르면 끝! https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation Table 4. Suppo..

papababo.tistory.com

 

 

로그인 해보면 성공

 

-------

권한리턴하기

유저 권한 리턴하기 (주석은 Arrays)

스프링 내부에서 원하는 GrantedAuthority 타입으로만 설정해서 리스트에 저장하여 리턴

 

 

----------

 

구조

 

PrincipalDetails.java

더보기
package com.jaybon.securityEx01.config.auth;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.jaybon.securityEx01.model.User;

import lombok.Data;


//Authentication 객체에 저장할 수 있는 유일한 타입
@Data
public class PrincipalDetails implements UserDetails{
	
	private User user;
	
	public PrincipalDetails(User user) {
		super();
		this.user = user;
	}
	
	

	@Override // 사용자의 비밀번호를 알고 싶으면 호출
	public String getPassword() {
		return user.getPassword();
	}

	@Override // 사용자의 유저네임를 알고 싶으면 호출
	public String getUsername() {
		return user.getUsername();
	}

	@Override // 사용자가 만료된 지를 알고 싶으면 호출
	public boolean isAccountNonExpired() { // 만료안됐니?
		//접속시간확인하여 true false 리턴
		return true;
	}

	@Override 
	public boolean isAccountNonLocked() { // 락 안걸렸니?
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isEnabled() { // 계정활성화 되어있니?
		return true;
	}
	
	// Arrays.asList(new SimpleGrantedAuthority(user.getRole()));
	@Override // 어떤 권한을 가졌니?
	public Collection<? extends GrantedAuthority> getAuthorities() {
		
		Collection<GrantedAuthority> authList = new ArrayList<>();
		authList.add(new SimpleGrantedAuthority(user.getRole()));
		return authList;
		
	}

}

PrincipalDetailsService.java

더보기
package com.jaybon.securityEx01.config.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.jaybon.securityEx01.model.User;
import com.jaybon.securityEx01.repository.UserRepository;

// UserDetailsService는  IoC로 찾음

@Service // UserDetailsService타입으로 메모리에 뜬다 (덮어씌워짐)
public class PrincipalDetailsService implements UserDetailsService{
	
	@Autowired
	private UserRepository userRepository;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// 어썬티케이션 매니저가 낚아챔
		// JPA는 기본적인 CRUD만 있어서 다른걸 쓰려면 만들어줘야함 
		
		User user = userRepository.findByUsername(username);
		
		if(user == null) {
			return null;
		}
		
		return new PrincipalDetails(user);
	}

}

SecurityConfig.java

더보기
package com.jaybon.securityEx01.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration // IoC 빈(bean, 인스턴스)을 등록
@EnableWebSecurity // 필터 체인 관리 시작 어노테이션
@EnableGlobalMethodSecurity(prePostEnabled = true) // 컨트롤러 접근 전에 낚아챔, 특정 주소 접근시 권한 및 인증 미리체크
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
	@Bean // IoC에 등록되어 컨피그가 호출될 때 생성, 메서드를 IoC하는 방법
	public BCryptPasswordEncoder enc() { // 마땅히 둘 곳이 없어서 둔 것 Controller를 제외한 곳에 둠
		return new BCryptPasswordEncoder();
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		http.csrf().disable(); // csrf 비활성화
		
		http.authorizeRequests()
			.antMatchers("/user/**", "/admin**") 
			.authenticated()
			.anyRequest()
			.permitAll()
			.and()
			.formLogin()
			.loginPage("/login") // 인증이 필요한 곳에 /login 으로 리다이렉트
			.loginProcessingUrl("/loginProc") // 필터체인에서 인지하고 있다가 시큐리티가 낚아채서 Authentication Manager
			.defaultSuccessUrl("/"); // 성공하면 해당 주소로 이동 / 슬래시만 달면 이전 주소로 이동
	}
}

IndexController.java

더보기
package com.jaybon.securityEx01.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.jaybon.securityEx01.config.auth.PrincipalDetails;
import com.jaybon.securityEx01.model.User;
import com.jaybon.securityEx01.repository.UserRepository;

@Controller
public class IndexController {
	
	@Autowired
	private UserRepository userRepository;
	
	@Autowired
	private BCryptPasswordEncoder bCryptPasswordEncoder;

	@GetMapping({ "", "/" })
	public @ResponseBody String index() {
		return "인덱스 페이지입니다";
	}

	@GetMapping("/user")
	public @ResponseBody String user(@AuthenticationPrincipal PrincipalDetails principalDetails) {
		System.out.println("확인"+principalDetails);
		System.out.println(principalDetails.getUser().getRole());
		System.out.println(principalDetails.getAuthorities()); // 출력 했을 때 사용자의 모든 권한을 리턴
		return "유저 페이지입니다";
	}

	@GetMapping("/admin")
	public @ResponseBody String admin() {
		return "어드민 페이지입니다";
	}

	@GetMapping("/login")
	public String login() {
		return "login"; // 머스태치를 pom에 추가했으니 서픽스는 templates 프리픽스는 .mustatche
	}

	@GetMapping("/join")
	public String join() {
		return "join";
	}

	@PostMapping("/joinProc")
	public String joinProc(User user) {
		System.out.println("회원가입 진행" + user);
		String rawPassword = user.getPassword();
		String encPassword = bCryptPasswordEncoder.encode(rawPassword);
		user.setPassword(encPassword);
		user.setRole("ROLE_USER");
		userRepository.save(user);
		return "redirect:/";
	}

}

User.java

더보기
package com.jaybon.securityEx01.model;

import java.sql.Timestamp;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import org.hibernate.annotations.CreationTimestamp;

import lombok.Data;

// ORM - Object Relation Mapping - 알아서 데이터베이스 테이블 만들어 준다

@Data
@Entity // 이것을 토대로 데이터베이스 모델을 만들 수 있다.
public class User {
	@Id // primary key를 걸어주는 어노테이션
	@GeneratedValue(strategy = GenerationType.IDENTITY) // 오라클은 시퀀스 전략, mysql은 오토인크리먼트 전략
	private int id;
	private String username;
	private String password;
	private String email;
	private String role; // ROLE_USER / ROLE_ADMIN
	@CreationTimestamp
	private Timestamp createDate;
}

UserRepository.java

더보기
package com.jaybon.securityEx01.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.jaybon.securityEx01.model.User;

// JpaRepository를 상속하면 자동 스캔됨.
public interface UserRepository extends JpaRepository<User, Integer>{

	//Jpa Naming 전략
	// SELECT * FROM user WHERE username = ?
	User findByUsername(String username); // 함수이름에 맞게 쿼리가 동작한다
	
//	// SELECT * FROM user WHERE username = ? AND password = ?
//	User findByUsernameAndPassword(String username, String password);
//	
//	@Query(value = "SELECT * FROM user", nativeQuery = true)
//	User 내맘대로();
	
}

join.mustache

더보기
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입 페이지</title>
</head>
<body>
	<h1>회원가입 페이지</h1>
	<hr/>
	<form action="/joinProc" method="post">
		<input type="text" name="username" placeholder="username">
		<input type="password" name="password" placeholder="password">
		<input type="email" name="email" placeholder="email">
		<button>회원가입</button>
	</form>
</body>
</html>

login.mustache

더보기
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인 페이지</title>
</head>
<body>
	<h1>로그인 페이지</h1>
	<hr/>
	<!-- 시큐리티는 x-www-form-urlencoded 타입만 인식 -->
	<form action="/loginProc" method="post">
		<input type="text" name="username">
		<input type="password" name="password">
		<button>로그인</button>
	</form>
</body>
</html>

application.yml

더보기
server:
  port: 8080
  servlet:
    context-path: /
    encoding:
      charset: utf-8
      enabled: true
      force: true
      
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Seoul
    username: cos
    password: cos1234
    
#  mvc:
#    view:
#      prefix: /templates/
#      suffix: .mustache


  jpa:
    hibernate:
      ddl-auto: update #create update none 3개 중 하나 선택
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    show-sql: true
      
      
      

 

 

 

'Server > Spring Boot' 카테고리의 다른 글

spring boot // 리플렉션  (0) 2020.08.01
spring boot // 필터 / 인터셉터  (0) 2020.08.01
spring boot // 블로그 연습  (0) 2020.07.27
spring boot // 주의사항  (0) 2020.07.27
spring boot // web.xml  (0) 2020.07.27

+ Recent posts