출처:
https://www.youtube.com/watch?v=pHp2LGuukls&list=PL93mKxaRDidECgjOBjPgI3Dyo8ka6Ilqm&index=54
로그인 화면은 35행 주소로 가고
36행은 시큐리티가 유저아이디 가져가서 처리하는 url주소이다.
그때 만들어야할 클래스가 있다
class PrincipalDetail implements UserDetails{}
를 만들자
스프링 시큐리티가 로그인 요청을 가로채서 로그인을 진행하고 완료가 되면 UserDeatials 타입의 오브젝트를
스프링 시큐리티의 고유한 세션저장소에 저장을 해준다.
GrantedAuthority는 추상메서드를 1개만 가진 인터페이스이다.
그러니 굳이 인터페이스를 불러오지말고
메서드를 불러오자
그런데
자바에서는 메서드를 파라미터에 못 넣으니 람다식으로 써준다.
이렇게 써준다.
그다음에
시큐리티가 대신 로그인해주는데 password를 가로채기를 하는데
해당 password가 뭘로 해쉬가 되어 회원가입이 되었는지 알아야
같은 해쉬로 암호화해서 DB에 있는 해쉬랑 비교할 수 있음.
저 null에 값 넣어줘야 비교가능하다.
JPA네이밍 전략으로 메서드 만들어주고
27행에 null이므로
이렇게 해준다
그러면 실행될때 해당 함수가 자동으로 실행되면서
findByUsername으로 유저를 찾는다.
패스워드는 알아서 처리
27행에 시큐리티의 세션에 유저정보가 저장된다.
22~27행 구현 안하면 유저정보를 시큐리티 username에 담을 수 없다.
36행 안만들면 패스워드 비교 못한다.
이렇게하면 username 찾아진다는걸 확인할 수 있다.
이건 안 됨.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="../layout/header.jsp"%>
<div class="container">
<form action="/auth/loginProc" method="POST">
<div class="form-group">
<label for="email">Username</label>
<input type="text" name="username" class="form-control" placeholder="Enter Username" id="username">
</div>
<div class="form-group">
<label for="pwd">Password</label>
<input type="password" name="password" class="form-control" placeholder="Enter password" id="password">
</div>
<button id="btn-login" class="btn btn-primary">로그인</button>
</form>
</div>
<!-- 로그인때 자바스크립트 사용 안할거임.
<script src="/js/user.js"></script> -->
<%@ include file="../layout/footer.jsp"%>
package com.cosblog.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
import com.cosblog.config.auth.PrincipalDetailService;
//빈 등록 : 스프링 컨테이너에서 객체를 관리할 수 있게 하는 것
//아래 3개는 세트라고 생각해라
@Configuration //빈등록 IOC관리
@EnableWebSecurity //시큐리티 필터가 등록이 된다.
@EnableGlobalMethodSecurity(prePostEnabled = true)//특정 주소로 접근을 하면 권한 및 인증을 미리 체크하겠다는 뜻.
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private PrincipalDetailService principalDetailService;
@Bean //IOC가 됨
public BCryptPasswordEncoder encodePWD() {
return new BCryptPasswordEncoder();
}
//시큐리티가 대신 로그인해주는데 password를 가로채기를 하는데
//해당 password가 뭘로 해쉬가 되어 회원가입이 되었는지 알아야
//같은 해쉬로 암호화해서 DB에 있는 해쉬랑 비교할 수 있음.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(principalDetailService).passwordEncoder(encodePWD());
}
@Override
protected void configure(HttpSecurity http)throws Exception{
http
.csrf().disable() //csrf 토큰 비활성화 (테스트시 걸어두는 게 좋음)
.authorizeRequests()
.antMatchers("/", "/auth/**", "/js/**", "/css/**", "/image/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/auth/loginForm")
.loginProcessingUrl("/auth/loginProc")//스프링 시큐리티가 해당 주소로 요청오는 로그인을 가로채서 대신 로그인 해준다.
.defaultSuccessUrl("/");
//.failureUrl("/fail");//실패시 보낼 Url위치 적는 방법
}
}
package com.cosblog.config.auth;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.cosblog.model.User;
//스프링 시큐리티가 로그인 요청을 가로채서 로그인을 진행하고 완료가 되면 UserDeatils 타입의 오브젝트를
//스프링 시큐리티의 고유한 세션저장소에 저장을 해준다.
public class PrincipalDetail implements UserDetails {
private User user;//콤포지션 :객체를 품고 있는거
public PrincipalDetail(User user) {
this.user = user;
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return user.getPassword();
}
@Override
public String getUsername() {
// TODO Auto-generated method stub
return user.getUsername();
}
//계정이 만료되지 않았는지 리턴한다. (true:만료안됨)
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
//계정이 잠겨있지 않았는지 리턴한다. (true:잠기지 않음)
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
//비밀번호가 만료되지 않았는지 리턴한다.(true:만료안됨)
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
//계정이 활성화(사용가능)인지 리턴한다.(true:활성화)
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return true;
}
//계정이 갖고있는 권한 목록을 리턴한다. (권한이 여러개 있을 수 있어서 루프를 돌아야 하는데 우리는 한개만)
//권한이 여러개면 for문을 돌려야 함.
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collectors = new ArrayList<>();
collectors.add(()->{return "ROLE_"+user.getRole();});
return collectors;
}
}
package com.cosblog.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.cosblog.model.User;
import com.cosblog.repository.UserRepository;
@Service
public class PrincipalDetailService implements UserDetailsService{
@Autowired
private UserRepository userRepository;
//스프링이 로그인 요청을 가로챌 때, username, password 변수 2개를 가로채는데
//password 부분 처리는 알아서 함.
//username이 DB에 있는지 확인해주면 됨.
@Override
public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException{
User principal = userRepository.findByUsername(username)
.orElseThrow(()->{
return new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다.:"+username);
});
return new PrincipalDetail(principal); //시큐리티의 세션에 유저 정보가 저장이 됨.
}
}
package com.cosblog.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import com.cosblog.model.User;
//DAO
//자동으로 bean등록이 된다.
//@Repository 생략가능하다
public interface UserRepository extends JpaRepository<User, Integer>{
//SELECT * FROM user WHERE username = 1?;
Optional<User> findByUsername(String username);
}
//JPA Naming 전략
//SELECT * FROM user WHERE username =?1 AND password =?2;
//User findByUsernameAndPassword(String username,String password);
//위 아래 2가지 방법 다 가능.
//@Query(value="SELECT * FROM user WHERE username = ?1 AND password = ?2", nativeQuery = true)
//User login(String username, String password)
package com.cosblog.controller;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import com.cosblog.config.auth.PrincipalDetail;
@Controller
public class BoardController {
@GetMapping({"","/"})
public String index(@AuthenticationPrincipal PrincipalDetail principal) {
System.out.println("로그인 사용자 아이디:"+principal.getUsername());
return "index";
}
}
'Spring > Spring boot' 카테고리의 다른 글
스프링부트 강좌 54강(블로그 프로젝트) - 글목록보기 (0) | 2022.01.25 |
---|---|
스프링부트 강좌 53강(블로그 프로젝트) - 글쓰기 완료 (0) | 2022.01.24 |
스프링부트 강좌 51강(블로그 프로젝트) - XSS와 CSRF (0) | 2022.01.24 |
스프링부트 강좌 50강(블로그 프로젝트) - 비밀번호 해쉬 후 회원가입하기 (0) | 2022.01.24 |
스프링부트 강좌 49강(블로그 프로젝트) - 스프링 시큐리티 로그인 페이지 커스터마이징 (0) | 2022.01.23 |
댓글