package com.example.security.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
@EnableWebSecurity //스프링 시큐리티 필터가 스프링 필터체인에 등록이 됨.
@EnableGlobalMethodSecurity(securedEnabled = true) //secured 어노테이션 활성
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//@Bean을 적으면 해당 메서드의 리턴되는 오브젝트를 IoC로 등록해준다.
@Bean
public BCryptPasswordEncoder encodePwd() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // 비활성화
http.authorizeRequests()
.antMatchers("/user/**").authenticated() //인증만되면 들어갈 수 있는 주소
.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll() //다른 요청들은 전부 permitAll
.and()
.formLogin()
.loginPage("/loginForm")
.loginProcessingUrl("/login") //이렇게 해두면 "/login" 주소가 호출이 되면 시큐리티가 낚아채서 대신 로그인을 진행시켜준다. -> 그러면
// 컨트롤러에 /login을 만들지 않아도된다. 시큐리티가 대신 로그인을 진행시켜주기 때문에
.defaultSuccessUrl("/"); // main 페이지로 가게하기
// /user로하면 403 에러가 뜨는 데 이것은 접근 권한이 없다는 뜻이다.
//로그인을 안했기 때문에, manager, admin도 마찬가지이다.
// 다른것들은 잘가짐, permitAll했기 때문이다.
//스프링 시큐리티가 낚아 채지 않음, 비활성화됨
}
}
@PostMapping("/join")
public String join(User user) {
user.setRole("ROLE_USER");
// userRepository.save(user); // 회원가입이 잘 됨, 비밀번호가 1234 -> 이렇게 되면 시큐리티로 로그인을 할 수 없음
// -> 이유는 패스워드가 암호화가 안되었기 떄문이다.
// 따라서 패스워드 암호화를 해야함 -> securityConfig에서
String rawPassword = user.getPassword();
String encPassword = bCryptPasswordEncoder.encode(rawPassword);
user.setPassword(encPassword);
userRepository.save(user);
return "redirect:/loginForm";
}
//SecurityConfig에 있는
//@EnableGlobalMethodSecurity(securedEnabled = true) //secured 어노테이션 활성
// 이부분에 의해 특정메소드에 간단하게 이렇게 걸고 싶을 때 사용
@Secured("ROLE_ADMIN")
@GetMapping("/info")
public @ResponseBody String info() {
return "개인정보";
}
package com.example.security.config.auth;
//시큐리티가 /login 주소 요청이 오면 낚아채서 로그인을 진행시킨다.
// 로그인을 진행이 완료가 되면 시큐리티 session 을 만들어준다. (Security ContextHolder)라는 이 키값에 세션 정보를 저장한다.
// 이때 session에 들어갈 수 있는 정보는 즉, 시큐리티가 가지고 있는 session에 들어갈 수 있는 오브젝트가 있는데
// 오브젝트 타입 => Authentication 타입 객체이다.
// Authentication 안에 User정보가 있어야함.
// 이것도 클래스가 정해져 있음
// User오브젝트의 타입은 => UserDetails 타입객체
// 시큐리티가 가지고 있는 session 영역이 있다.
// 즉, 시큐리티 세션 영역에 세션 정보를 저장해주는데, 여기 들어갈 수 있는 객체 정해져있음 -> Authentication으로
// 이, Authentication에 User 정보를 저장할 때, User 정보는 UserDetails 타입이어야함
import com.example.security.model.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
public class PrincipalDetails implements UserDetails {
private User user; //콤포지션
public PrincipalDetails(User user) {
this.user = user;
}
//해당 User의 권한을 리턴하는 곳!!
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// user.getRole(); -> string 타입이라 리턴이 불가능
Collection<GrantedAuthority> collect = new ArrayList<>();
collect.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return user.getRole();
}
});
return collect;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
// 우리 사이트에서 1년동안 회원이 로그인을 안하면 휴면계정으로 하기로했다면
// User.getLoginDate()를 가져와서 저 날짜로부터 현재시간 - 저 날짜시간 해서 일년을 초과하면
// return 을 false로 하면됨
return true;
}
}
Security Session => Authentication => UserDetails(PrincipalDetails)
package com.example.security.config.auth;
import com.example.security.model.User;
import com.example.security.repository.UserRepository;
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;
// 이때 만족을함
// 시큐리티 설정에서 loginProcessingUrl("/login) 으로 걸어놨기 때문에
// /login 요청이 오면 자동으로 UserDetailsService 타입으로 IoC 되어 있는
//loadUserByUsername 함수가 실행이됨
@Service
public class PrincipalDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
//시큐리티 session => Authentication = > UserDetails
// 이게 리턴이 되면
//Authentication(UserDetails) 이렇게 Authentication 내부에 쏘옥 들어감
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User userEntity = userRepository.findByUsername(username);
if (userEntity != null) {
return new PrincipalDetails(userEntity);
}
return null;
}
}
저기 있는 username을 받는 곳이 PrincipalDetailsService의 loadUserByUsername(String username) 함수의 파라미터
만약 loginForm.html에 username2로 받는다면 저기 안들어감!!!
인프런강의 시큐리티 무료강의를 수강했습니다.