728x90

1. 시큐리티Config 파일에서 .access("hasRole('ADMIN')") 을 이용한다.

.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")

 

 * 시큐리티Config 전체 코드 참고 : https://black-mint.tistory.com/11

 

[Spring Boot] 1. Security 사용 설정, 비밀번호 인코딩

1. 의존성 추가  - build.gradle 에 다음 의존성을 추가해준다. implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.security:spring-security-test'..

black-mint.tistory.com

 

2. @EnableGlobalMethodSecurity를 사용한다.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http
                .authorizeRequests()
                    .antMatchers("/css/**").permitAll()
                    .antMatchers("/board/**").authenticated()
                    .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") // 권한 설정
                    .anyRequest().permitAll() 
                    .and()
                .formLogin()
                    .loginPage("/loginForm") // 로그인 페이지
                    .loginProcessingUrl("/login")
                    .defaultSuccessUrl("/")
                    .and()
                .logout()
                    .permitAll();
    }
}
  • 시큐리티 Config파일 상단에 @Secured 어노테이션과 @preAuthorize를 활성화하는 @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)을 추가한다. 
  • 컨트롤러에 @Secured를 추가해서 페이지 권할을 설정한다.
@Secured("ADMIN")
@GetMapping("/admin")
public String admin() {
    return "/admin";
}
  • 2개 이상 권한을 주고 싶을 때는 @PreAuthorize를 추가하면 된다.
@PreAuthorize("hasRole('ADMIN') or hasRole('MANAGER')")
@GetMapping("/admin")
public String admin() {
    return "/admin";
}

 

-

 

* 해당 글은 인프런에 게시된 '최주호'님의 강의를 듣고 개인적으로 정리한 글입니다.

강의 출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0

728x90

작업의 루틴은 다음과 같다.

  1. 시큐리티가 로그인을 진행시키면 UserDetails타입의 객체를 실행한다.
  2. 로그인 진행이 완료되면 시큐리티 session을 생성하고 Security ContextHolder라는 키 값으로 세션 정보 저장한다.
  3. 키에 대한 값으로 들어가는 오브젝트 타입은 Authentication이다.
  4. Authentication 안에는 User(UserDetails타입) 정보가 있어야 됨.

1. 시큐리티가 로그인을 진행시키면 실행 될 UserDetails타입의 클래스를 생성하기 위해 UserDetails를 implements한다.

public class PrincipalDetails implements UserDetails {

    private Member member;

    public PrincipalDetails(Member member) {
        this.member = member;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> collection = new ArrayList<>();
        collection.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                return member.getRole();
            }
        });
        return collection;
    }

    @Override
    public String getPassword() {
        return member.getPassword();
    }

    @Override
    public String getUsername() {
        return member.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
  • getAuthorities() : 해당 유저의 권한을 리턴한다. 리턴 타입이 GrantedAuthority이므로 Collection을 이용해서 반환 타입을 설정
  • isAccountNonExpired() : 계정이 만료됐는지 체크
  • isCredentialsNonExpired() : 비밀번호를 오래 사용했는지 체크
  • isEnabled() : 계정의 활성화 여부를 체크

2. 로그인 요청이 오면 자동으로 UserDetailsService 타입으로 IoC되어 있는 loadUserByUsername 함수가 실행되므로 UserDetailsService를 implements하는 서비스 클래스 생성

@Service
public class PrincipalDetailsService implements UserDetailsService {

    @Autowired
    private JpaMemberRepository jpaMemberRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Member memberEntity = jpaMemberRepository.findByUsername(username);

        if(memberEntity != null) {
            return new PrincipalDetails(memberEntity);
        }
        return null;
    }
}
  • 파라미터로 받는 username은 form에서 받는 input태그와 name이 같아야 한다.
  • 다를 경우 시큐리티 config 파일에서 수정할 수 있다. (userNameParameter()이용)

결국 Security Session 안 -> Authentication객체 안 -> UserDetails(PrincipalDetails) 형식이 완성되고 시큐리티에 의해 로그인이 완료된다.

 

-

 

* 해당 글은 인프런에 게시된 '최주호'님의 강의를 듣고 개인적으로 정리한 글입니다.

강의 출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0

728x90

JPA를 사용하면서 DTO랑 Entity를 따로 둘 필요가 있나? 라는 의문점이 생겨 간단하게 정리한다.

1. Entity : 실제 DB테이블과 1:1로 매핑 되는 클래스

  • DB테이블에 존재하는 컬럼만을 속성(필드)로 가져야 한다.
  • 최대한 해당 클래스 안에서 필요한 로직을 구현 (외부에서 getter 사용을 최소화)
  • setter 금지 (데이터 값이 무분별하게 바뀌면 위험)

2. DTO : 데이터 전송 객체(Data Transfer Object)이라는 의미라는 가졌으며, 주로 비동기 처리에 사용한다.

  • DB데이터를 서비스나 컨트롤러에 데이터를 전송할 때 사용된다.
  • getter/setter 메소드만 가진다. (로직X)

3. VO : DTO와 같은 개념이지만 read only 속성을 가진다.

  • 특정한 비즈니스 값을 담는 객체
728x90

문제점 : MySQL 데이터데이스에 defalut값을 설정해놨으나, SpringBoot에서 JPA로 데이터를 Insert할 때 값을 넣지 않으면 default값이 아닌 null이 들어갔다. 음.. 빈 값이 아닌 null이 들어가서 그런 것 같다.

 

해결 : @DynamicInsert와 @DynamicUpdate를 @Entity에 같이 작성해준다.

@DynamicInsert : insert 시 null인 필드 제외

@DynamicUpdate : update 시 null인 필드 제외

728x90

1. 의존성 추가

 - build.gradle 에 다음 의존성을 추가해준다. 

 

implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-test'

 

 

 - Maven의 경우 pom.xml 파일에 다음을 추가한다.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-test</artifactId>
  <scope>test</scope>
</dependency>

2. 시큐어Configure 파일을 생성해서 다음의 코드를 작성해준다.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http
                .authorizeRequests()
                    .antMatchers("/css/**").permitAll()
                    .antMatchers("/board/**").authenticated()
                    .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") // 권한 설정
                    .anyRequest().permitAll() 
                    .and()
                .formLogin()
                    .loginPage("/loginForm") // 로그인 페이지
                    .loginProcessingUrl("/login")
                    .defaultSuccessUrl("/")
                    .and()
                .logout()
                    .permitAll();
    }
}
  • @EnableWebSecurity : 스프링 시큐리티 필터가 스프링 필터체인에 등록된다.
  • BCryptPasswordEncoder : 유저의 패스워드를 암호화하는 인코더
  • authorizeRequests() : 시큐리티 처리에 HttpServletRequest를 이용하겠다는 의미
  • antMatchers() : 특정 경로 지정
  • permitAll() : 접근 허용
  • authenticated() : authorizeRequests에 적힌 경로는 로그인 후에 접근 가능
  • access : 주어진 SpEL표현식의 결과가 true이면 접근 허용
  • hasRole('ROLE_ADMIN') : 권한이 ROLE_ADMIN인 경우만 접근 가능
  • anyRequest().permitAll() : 그 외 요청은 허용
  • loginProcessingUrl("/login") : 시큐리티에서 로그인을 대신 진행하도록 정의 -> 컨트롤러에 안만들어도 됨.

 

3. 비밀번호 인코딩

@PostMapping("/join")
public String join(Member member){ // view의 form->input 의 name과 매핑됨.
    String encPwd = bCryptPasswordEncoder.encode(member.getPassword());
    member.setPassword(encPwd);

    memberService.join(member);

    return "redirect:/loginForm";
}
  • 컨트롤러에서 BCryptPasswordEncoder 객체를 이용해서 encode 함수를 통해 인코딩 한다.

-

 

 

* 해당 글은 인프런에 게시된 '최주호'님의 강의를 듣고 개인적으로 정리한 글입니다.

강의 출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0

728x90

1. MySQL에서 테이블을 만들어준다.

ID에 적용할 것이므로 AI(Auto Increment) 체크!

 

2. 자바 코드에서 Model에 @GeneratedValue 어노테이션을 추가해준다.

@GeneratedValue(strategy = GenerationType.IDENTITY)

다음은 코드에 적용한 모습이다.

 

+ Recent posts