일단 기본셋팅에서는 DB 추가, 조회하는 방법 글에서 동일하다 다른 한가지는 spring.io에서 spring Security하나가 더 추가 됬을뿐이다.
프로젝트를 만들고 셋팅도 다했으면 확인먼저하자!!
시큐리티를 추가하면 이런것들이 셋팅이 되어있다 확인!!!
일단 패키지를 만들어주자
프로젝트 하위에 (config), (domain 하위 user), (service 하위 auth), (web 하위 controller, dto) 크게 이렇게 4개의 패키지를 준비해서 추가적인건 패키지와 클래스를 생성할거다!
PageController 이라는 클래스를 하나만들어 @Controller를 해주고 Getmapping 3개의 메소드를 만들어주자
그리고나서 templates폴더 하위에 index.html을 생성해주고나서, auth폴더 하위에 signin.html, signup.html 2개의 html을 더만들어준다.
index.html에는 메인페이지라고 임시로 써주자!
이제 서버를 실행을 해보자
서버를 실행후 인터넷에 localhost:8000/login 으로 접속을하면 시큐리티에서 지원해주는 로그인창이 뜬다
무조건 로그인에 걸린다. 초기 아이디는 "user"이고 패스워드는 서버가 실행시 시큐리티 자체에서 지원해주는 비밀번호가있다 그걸로 입력하면된다.
그럼 메인페이지로 넘어가게된다.
로그인은 이렇게 하면되고 로그아웃은 주소창에 login을 logout로 바꿔주면된다.
아이디와 비밀번호가 잘못입력했을시에는 이렇게 주소창에 error이라고 뜨면서 자격 증명에 실패라고 뜬다
이렇게 기존에 시큐리티에서 제공해주는 아이디와 패스워스로는 계속 사용하지 않을거다 직접 아이디와 비번을 부여해서 사용할거다 근데 이 방법은 아이디와 비밀번호를 DB에서 저장되어있는 것을 사용하는 것이아니라 직접 서버에 부여를 해서 사용하는 것이다
서버에 직접 아이디와 비밀번호를 부여를 하려면 application.yml에서 설정을 할 수 가있다.
이제는 기존에 있는 시큐리티를 쓰지않고 직접 본인이 시큐리티를 만들어 써볼거다
그것을 쓰려면 일단 처음에 패키지를 셋팅을할때 config라는 패키지를 만들었다
config하위에 SecurityConfig클래스를 하나 만든다.
그리고나서 WebSecurityConfigurerAdapter을 상속을 받는다.
WebSecurityConfigurerAdapter가 있어야 시큐리티가 동작을 할수있기때문이다.
configure는 3가지 종류가 있다 우리가 지금 쓸것은 http관련된 configure를 쓸것이다.
여기서 제일 중요한 부분이 있다 @EnableWebSecurity이다. 이것은 기존의 WebSecurityConfigurerAdapter를 비활성화 시키고 현재 시큐리티를 따르겠다 라는 의미이다. 이게 없으면 나만의 시큐리티를 사용할수가 없는것이다.
@Configuration은 IoC 등록하는거다.
그 다음으로 중요한것은 http를 매개변수로 받아서쓸거다 http의 형식은 build형식이라서 객체, 반환, 객체, 반환 계속 이런식으로 해주기때문이다.
그리고 csrf()를 비활성화 즉 사용을 안하겠다는 것이다. 이것이 뭐냐!!!!
ex) auth/signin(로그인 웹페이지)를 보여달라고 서버에게 요청을했다 그럼 서버에서는 html을 즉 로그인창을 클라이언트에게 보여줄것이다.이때 html를 돌려줄때 input(아이디, 비밀번호)에다가 토큰을 발급을 해준다.이 토큰이 바로 csrf()라는 것이다.
그럼 input에 입력을 하고나서 reques 요청(submit요청)을 날릴것이다. 그럼 서버는 제일 먼저 확인하는게 본인서버가 발급해준 토큰이 있는지부터 확인을 한다.중간에 위조가 된 것인지!! postman이라던지 임시 웹페이지를 만들어 주소만 바꿔서
어느곳에서 요청을 보내면 그 해당하는 페이지가 뜨면 안되기때문이다 그래서 그것을 방지하기위해 쓰는것이다. 오로지
해당하는 서버가 발급해준 요청만 유효하다라는 의미.
csrf()토큰을 설정해주는 과정은 많이 복잡하다. 그방법 x
input들이 들어가는 요청 모두다 토큰을설정해줘야 하기때문이다. 그런데 나중엔 jwt 웹토큰을 사용할거라서
일단 csrf().disable를 해준다 안해주면 유효하지않는 요청이라 에러가 뜬다!
http.authorizeRequests() : 요청이 들어왔을때의 인증할겠다 셋팅을 할때 지정해주는거다!
antMatchers("/","/index") : /와 index의 주소를 지정해주는거다 나중에 뒤에서 임의지정주소로 인증이 필요하다는걸 가리키는 주소이다.
authenticated() : 위에서 말했던 인증을 하겠다는 의미이다. 위에꺼와 같이 쓴다고 보면된다.
anyRequest() : antMatchers()에서 지정한 주소 이외의 다른 모든 주소를 의미하는거다.
permitAll() : 모든 권한을 줘라 즉, 인증을 거칠 필요가 없다는 의미이다.
and() : 영어뜻 그대로 그리고라는 뜻이다. 위에게 하나의 셋트라고 보면된다.
formLogin() : 로그인방법에는 3가지가 있다. 그중에 하나인 formLogin방식이다. 나머지는 httpBasic방식과 JWT토큰방식이다. formLogin방식 제일 일반적인 방식이다.
loginPage( "/auth/signin" ) : /auth/signin 여기 주소로 로그인 시도를 하겠다는 의미이다. 여기서 로그인시도할때의
요청은 get요청이다.
loginProcessingUrl : 로그인요청이다. 즉, post요청이다.
formLogin에서 로그인페이지가 떴을거다. 그건 loginPage("auth/signin") 여기서 뜬것이다. 이건 pageController에서 getmapping로 만들어 뒀다. antmatchers에서 지정한 주소("/" , "index")에 authenticated에서 인증이 걸리게되면
loginPage로 요청을 보내게 된다. 중간에서 자동으로 우리가 직접 get요청을 해도되지만 원래는 요청을 보내게되면 서블릿을 가야한다. 근데 가기전에 loginPage에서 낚아채는것이다. 왜냐고??? 이유는 antmatchers에서 주소를 지정을 해줬기 때문이다.
formLogin에서 아이디와 비번을 입력을 할것이다 로그인 버튼이 있다 이게 submit 버튼이다. form에다가 action을 post요청을 걸어어두면 로그인버튼을 눌렀을때 post요청이 보내진다.
(로그인할때는 post요청!! 로그인정보는 주소창에 뜨면 안되기때문에!!!)
post요청을 loginProcessingUrl( "/auth/signin/" ) <---- 여기로 받겠다!!!
pageController에다가는 따로 post요청을 만들지 않는다. 이미 시큐리티 안에는 컨트롤러가 만들어졌기 때문이다.
풀어서 정리를 하게되면 우리가 지정한 요청주소에서 인증을거치게끔해주고나서 모든 요청들은 인증을 거칠필요없이 모든 권한을 주고나서, 로그인 방식은 from로그인 방식을 사용을하게끔 해준다. 그런데 다른 요청주소에서는 인증을 안거치지만 "/", "/inde" 주소의 요청에서는 인증을 거치게끔 해줬기때문에 loginPage("/auth/signin") 주소로 요청이 날라가서 인증하게끔 로그인창 주소인 로그인창 화면이 띄워지는 것이다.
일단 페이지 하나를 더 만들어보자 mypage.html
PageController에서 페이지를 열수있게끔 mypage get요청을 할수있게끔 하나 만들어준다!
그리고나서 SecurityConfig에서 antmatchers() 안에 /mypage/** 를 추가해준다. 즉, mypage뒤에 붙은 모든 주소들은 인증을 거치라는 의미이다. 쉽게생각하면 어떤 사이트에 로그인 해야지만 볼수있는 데이터 개인의 회원정보라고 생각하면된다. 회원정보는 로그인을 하지않으면 사이트를 열수없고 로그인창으로 보내진다. 이렇게 생각하면 개념을 이해하기에 쉽다.
테스트를 해보기위해 임시로 로그인창을 간단하게 만들어볼거다. templates 하위에 auth 하위에 signin.html파일을 하나 만들어주고나서 로그인창을 만든다.
위에서도 말했지만 로그인할때 아이디와 비밀번호는 get요청이 아닌post요청이다. 그리고 로그인창 자체에 액션을 post를 걸어준다고했는데 그주소가 action="/auth/signin" 이다.
서버를 실행시키고 이제 주소창에 로그인창을 띄워보고 테스트를 해보자! 이건 지금 get요청으로 받아온 페이지라걸 알수가있다. 하지만 우리는 서버에 설정해둔 아이디, 비밀번호를 사용하는게 아니라 DB에저장되어있는 아이디, 비밀번호를 사용할거다 application.yml에서 주석처리를 해두자!
주석처리를 했으니 이제 설정해둔 아이디, 비밀번호로는 로그인 안된다는걸 알수가 있다!
여기서 확실히 알수있는건 로그인 요청을하면 post요청으로 날아간다!!
즉, loginProcessingUrl("/auth/signin") <<< 이게 날아간다는 뜻이다.
이제는 우리가 loginProcessingUrl을 커스텀을 해줄거다 그럴려면 사용할 Service가 필요하다.
service 하위 auth 하위에 PrincipalDetailsService클래스를 하나 만들어주자!!!
@Service를 달아주고나서 구현을 해줄거다 UserDetailsService를 implements를 해줄거다
그리고나서 implements를 해줬으니 메소드들을 @Override해준다 그럼 loadUserByUsername가 만들어진다
loadUserByUsername가 가지고있는 return자료형은 UserDetails이다.
return을해주려면 UserDetails라는 객체를 하나 생성을해서 return을 해줘야한다. 그런데 인터페이스다
이렇게 생성을되면 익명클래스로 만들어서 일단 테스트를 해볼거다
getUsername(), getPassword에 임시로 값을 넣어준다. 원래는 getPassword는 암호가가 되있어야 한다.
getAuthorities()에는 리턴값을 userDetails를 넣어준다. 여기까지 설정을 다했으면 SecurityConfig로 넘어간다.
SecurityConfig에서 BCryptPasswordEncoder라는 암호화 모듈을 하나 만들거나 메소드명 사용하면된다.
리턴은 new BCryptPasswordEncoder로 반환해주면 된다.맨위에는 @Bean을 달아주면된다!!
이렇게 암호화모듈이 만들어졌으면 다시 PrincipalDetailsService로 넘어와서 getPassword()에 1234라는 비밀번호를
암호화를 시켜준다.
그리고나서 암호화가된 비밀번호가 잘 적용이 되있는지 확인해보자!!!
주소창에 localhost:8000/, localhost:8000/index, localhost:8000/mypage/** (** : 뒤에오는 모든 주소를 의미함)
이 세개의 주소를 다 검색을 하면 인증하도록 설정을해줬고 인증을 하도록 loginPage("/auth/signin")로 넘어가서 로그인을 하라고 페이지가 뜬다.
그리고 우리는 서버에 지정해놓은 아이디와 비밀번호를 작성한 application.yml에서 비활성화를 시켜줬다. 그런데
PrincipalDetailsService클래스에서 다시 getUsername(), getPassword() 값을 넣어주고나서
getPassword() 리턴을 BCryptPasswordEncoder().encode()를 해줬는데, encode()안에 암호화한 값인 1234를 입력해주면된다.
로그인 인증이 잘 되고나서 페이지 잘 뜨는걸 확인할수가 있다.
BCryptPasswordEncoder 꼭 등록을 해줘야한다. 그렇지 않으면 암화가 되지가 않는다!!
이제는 권한에 관련되는 부분이다. 먼저 PrincipalDetailsService에서 조건을 달아준다. 아이디가 잘못입력이 됬으면 예외를 발생시켜주는 조건이다.
그럼 SecurityConfig에서 예외를 발생해준걸 중간에 잡아줘야한다.
failureHandler 라는걸 써줄거다 이건 예외가 발생하면 중간에서 낚아채서 예외를 우리에게 알려주는 부분이다.
그럼 낚아채줘서 예외를 알려주게끔 클래스를 하나 만들어야한다.
AuthFailureHandler클래스를 만들어주고나서 이제 아이디를 틀리게해서 틀린 아이디를 잘 잡아주는지 확인해 보자!!
로그인 실패라고 잘 알려주는걸 확인했다!
우리는 계속 서버에 지정해둔 아이디, 비밀번호를 쓰지를 않는다 DB에서 저장을해둔 아이디와 비밀번호로 로그인 접속을 시도를 할거다 그럼 DB와 연결을 해야한다.
먼저 mappers폴더를 하나 만들어서 거기에 User.xml을 하나 만들자
그리고 domain.user안에 UserRepository 인터페이스를 하나 만들어 save()에는 회원가입,
findeUserByUsername()은 로그인시 사용을 할거다
User객체가 필요하니 하나 만들어주자
그리고나서 user_roles값을 ,(쉼표) 기준으로 배열을 만들어준다. 그리고나서 asList를 사용해서 리스트로 바꿔 retrun해준다.
service.auth에 PrincipalDetails클래스를 하나만들어주고 UserDetails를 implements(구현)을 해준다.
아까 위에서 PrincipalDetailsService에서 UserDetails를 사용했다 그런데 거기서 생성된 익명클래스들을 쓰지를 않을거다 싹 지워주자!
PrincipalDetailsService에서 UserDetails객체가 자기 자신을 리턴을 해줬다.
그래서 PrincipalDetails클래스에서 implements를 해주니깐 지웠던 익명클래스들을 만들수가 있는것이다.
그리고 false값 4개를 전부 true로 바꿔준다. 왜냐하면 일단 지금 테스트를 하기위에 모든 로그인은 허용을 해야한다.
<계정 만료 여부>
isAccountNonExpired() -> true : 만료안됨 // false : 만료
<계정 잠김 여부>
isAccountNonLocked() -> true : 잠기지 않음 // false : 잠김
<비밀번호 만료 여부>
isCredentialsNonExpired() -> true : 만료안됨 // false : 만료
<사용자 활성화 여부>
isEnabled() -> true : 활성화 // false : 비활성화
'Java Spring Boot' 카테고리의 다른 글
스프링 프레임워크란?? (0) | 2024.02.27 |
---|---|
SPRING BOOT MVC구조 DB데이터 (조회) (0) | 2022.08.01 |
SPRING BOOT MVC 구조 DB(추가) (0) | 2022.07.26 |
댓글