티스토리 뷰

728x90
반응형

스프링 시큐리티 - 사용자 정의

UserDetailsService

사용자 이름으로 사용자를 검색하는 역활

 

UserDetailsManager

- InMemoryUserDetailsManager

- JdbcUserDetailsManager

- LdapUserDetailsManager

 

위 3개의 클래스는 "UserDetailsManager"를 구현한 클래스이다.

- InMemoryUserDetailsManager = 메모리에서 사용자를 관리

- JdbcUserDetailsManager = DBMS를 이용해서 사용자를 관리

- LdapUserDetailsManager - Ldap를 이용해서 사용자를 관리

 

UserDetails

스프링 시큐리티가 이해할 수 있는 방식으로 사용자를 기술한다.

 

GrantedAuthority

사용자 권한

 

 

 

package com.example.demo.config;


import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import com.example.demo.model.CustomUser;
import com.example.demo.service.InMemoryUserDetailsService;

@Configuration
public class SpringSecurityConfig {
	
	@Bean
	UserDetailsService userDetailsService() {
		UserDetails user = new CustomUser("james", passwordEncoder().encode("12345"), "read");
		List<UserDetails> users = List.of(user);
		
		return new InMemoryUserDetailsService(users);
	}
	
	@Bean
	PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.httpBasic(Customizer.withDefaults()) // HTTP Basic 인증 활성화
			.authorizeHttpRequests(authorize -> authorize
				.anyRequest().authenticated()
			);
		
		return http.build();
	}
	
	@Bean
	CustomAuthenticationProvider customAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
		return new CustomAuthenticationProvider(userDetailsService, passwordEncoder);
	}

}
package com.example.demo.model;

import java.util.Collection;
import java.util.List;

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

public class CustomUser implements UserDetails {
	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
	
	private final String userName;
	private final String userPassword;
	private final String authority;
	
	public CustomUser(String userName, String userPassword, String authority) {
		this.userName = userName;
		this.userPassword = userPassword;
		this.authority = authority;
	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return List.of(() -> "read");
	}

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

	@Override
	public String getUsername() {
		return this.userName;
	}
	
	public String getAuthority() {
		return this.authority;
	}
	
	@Override
	public boolean isAccountNonExpired() {
		return true;
	}
	
	@Override
	public boolean isAccountNonLocked() {
		return true;
	}
	
	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}
	
	@Override
	public boolean isEnabled() {
		return true;
	}

}
package com.example.demo.service;

import java.util.List;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class InMemoryUserDetailsService implements UserDetailsService {
	private final List<UserDetails> users;
	
	public InMemoryUserDetailsService(List<UserDetails> users) {
		this.users = users;
	}

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		return users.stream()
				.filter(u -> u.getUsername().equals(username))
				.findFirst()
				.orElseThrow(() -> new UsernameNotFoundException("User not found"));
	}

}
package com.example.demo.config;

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

	private final UserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;

    public CustomAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }
    
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		String userName = authentication.getName();
		String userPassword = String.valueOf(authentication.getCredentials());
		UserDetails userDetails = this.userDetailsService.loadUserByUsername(userName);
		
		if(this.passwordEncoder.matches(userPassword, userDetails.getPassword())) {
			return new UsernamePasswordAuthenticationToken(userName, userPassword, userDetails.getAuthorities());
        } else {
            throw new BadCredentialsException("Invalid username or password");
        }
	}
 
	@Override
	public boolean supports(Class<?> authentication) {
		return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
	}

}
728x90
반응형