Skip to content
Snippets Groups Projects
Commit 66b2a5a5 authored by Gwan Ju's avatar Gwan Ju
Browse files

feat : add authentication functionality and login service

parent fcc322e3
No related branches found
No related tags found
1 merge request!1게시글 작성, 댓글 작성, 유저 관리 기능 추가
Showing
with 429 additions and 0 deletions
package umc.spring.post.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "DELETE")
.allowCredentials(false)
.maxAge(3000);
}
}
\ No newline at end of file
package umc.spring.post.config.security;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@RequiredArgsConstructor
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider jwtTokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String token = resolveToken((HttpServletRequest) request);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication authentication = jwtTokenProvider.getAuthentication(token);
System.out.println(authentication);
SecurityContextHolder.getContext().setAuthentication(authentication);
System.out.println(authentication);
}
chain.doFilter(request, response);
}
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer")) {
return bearerToken.substring(7);
}
return null;
}
}
\ No newline at end of file
package umc.spring.post.config.security;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.stream.Collectors;
@Component
public class JwtTokenProvider {
private final String secretKey;
public JwtTokenProvider( @Value("${jwt.secret}") final String secretKey) {
this.secretKey = secretKey;
}
public TokenInfo generateToken(Authentication authentication) {
System.out.println(authentication);
String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
System.out.println("auth " + authorities);
long now = (new Date()).getTime();
Date accessTokenExpiration = new Date(now + 3600000); // 1h
Date refreshTokenExpiration = new Date(now + 1209600000); // 14d
String accessToken = Jwts.builder()
.setSubject(authentication.getName())
.claim("auth", authorities)
.setExpiration(accessTokenExpiration)
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
String refreshToken = Jwts.builder()
.setExpiration(refreshTokenExpiration)
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
TokenInfo tokenInfo = new TokenInfo();
tokenInfo.setGrantType("Bearer");
tokenInfo.setAccessToken(accessToken);
tokenInfo.setRefreshToken(refreshToken);
return tokenInfo;
}
public Authentication getAuthentication(String accessToken) {
Claims claims = parseClaims(accessToken);
System.out.println("log" + claims);
if (claims.get("auth") == null) {
throw new RuntimeException("권한 정보가 없는 토큰입니다.");
}
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get("auth").toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
UserDetails principal = new User(claims.getSubject(), "", authorities);
return new UsernamePasswordAuthenticationToken(principal, "", authorities);
}
public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token);
return true;
} catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
System.out.println("Invalid JWT Token" + e);
} catch (ExpiredJwtException e) {
System.out.println("Expired JWT Token" + e);
} catch (UnsupportedJwtException e) {
System.out.println("Unsupported JWT Token" + e);
} catch (IllegalArgumentException e) {
System.out.println("JWT claims string is empty." + e);
}
return false;
}
private Claims parseClaims(String accessToken) {
try {
return Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(accessToken).getBody();
} catch (ExpiredJwtException e) {
return e.getClaims();
}
}
}
\ No newline at end of file
package umc.spring.post.config.security;
public enum Role {
USER("ROLE_USER"),
ADMIN("ROLE_ADMIN");
private final String value;
public String getValue() {
return value;
}
Role(String value) {
this.value = value;
}
}
\ No newline at end of file
package umc.spring.post.config.security;
import org.springframework.beans.factory.annotation.Autowired;
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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
@EnableWebSecurity
@Configuration
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Autowired
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.httpBasic(HttpBasicConfigurer::disable)
.csrf(CsrfConfigurer::disable)
.cors(Customizer.withDefaults())
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorize -> authorize.requestMatchers("/**").permitAll())
.addFilterBefore(jwtAuthenticationFilter, BasicAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
\ No newline at end of file
package umc.spring.post.config.security;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import umc.spring.post.data.dto.UserInfoDto;
public class SecurityUtil {
public static UserInfoDto getCurrentMemberId() {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || authentication.getName() == null) {
throw new RuntimeException("No authentication information.");
}
UserInfoDto userInfoDto = new UserInfoDto();
userInfoDto.setUserId(authentication.getName());
userInfoDto.setMemberRole(authentication.getAuthorities().stream().toList().get(0).toString().replaceAll("ROLE_", ""));
return userInfoDto;
}
}
\ No newline at end of file
package umc.spring.post.config.security;
import lombok.Data;
@Data
public class TokenInfo {
private String grantType;
private String accessToken;
private String refreshToken;
private String email;
private String memberRole;
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMemberRole() {
return memberRole;
}
public void setMemberRole(String memberRole) {
this.memberRole = memberRole;
}
}
\ No newline at end of file
package umc.spring.post.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import umc.spring.post.config.security.TokenInfo;
import umc.spring.post.data.dto.UserInfoDto;
import umc.spring.post.data.dto.UserJoinDto;
import umc.spring.post.data.dto.UserLoginDto;
import umc.spring.post.service.AuthService;
@RestController
@RequestMapping("/user")
public class AuthController {
@Autowired
private final AuthService authService;
public AuthController(AuthService authService) {
this.authService = authService;
}
@PostMapping("/login")
public TokenInfo login(@RequestBody UserLoginDto userLoginDto) {
return authService.login(userLoginDto);
}
@PostMapping("/register")
public void register(@RequestBody UserJoinDto userJoinDto) {
authService.join(userJoinDto);
}
@GetMapping("/info")
public UserInfoDto info() {
return authService.info();
}
}
package umc.spring.post.service;
import umc.spring.post.config.security.TokenInfo;
import umc.spring.post.data.dto.UserInfoDto;
import umc.spring.post.data.dto.UserJoinDto;
import umc.spring.post.data.dto.UserLoginDto;
public interface AuthService {
TokenInfo login(UserLoginDto userLoginDto);
void join(UserJoinDto userJoinDto);
UserInfoDto info();
}
package umc.spring.post.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import umc.spring.post.data.entity.User;
import umc.spring.post.config.security.JwtTokenProvider;
import umc.spring.post.config.security.Role;
import umc.spring.post.config.security.SecurityUtil;
import umc.spring.post.config.security.TokenInfo;
import umc.spring.post.data.dto.UserInfoDto;
import umc.spring.post.data.dto.UserJoinDto;
import umc.spring.post.data.dto.UserLoginDto;
import umc.spring.post.repository.UserRepository;
@Service
public class AuthServiceImpl implements AuthService, UserDetailsService {
@Autowired
private final UserRepository userRepository;
@Autowired
private final PasswordEncoder passwordEncoder;
@Autowired
private final JwtTokenProvider jwtTokenProvider;
public AuthServiceImpl(UserRepository userRepository, PasswordEncoder passwordEncoder, JwtTokenProvider jwtTokenProvider) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.jwtTokenProvider = jwtTokenProvider;
}
@Override
public TokenInfo login(UserLoginDto userLoginDto) {
User user = userRepository.findByUserId(userLoginDto.getUserId()).orElseThrow(() -> new UsernameNotFoundException("아이디 혹은 비밀번호를 확인하세요."));
boolean matches = passwordEncoder.matches(userLoginDto.getPassword(), user.getPassword());
if (!matches) throw new BadCredentialsException("아이디 혹은 비밀번호를 확인하세요.");
Authentication authentication = new UsernamePasswordAuthenticationToken(user.getUserId(), user.getPassword(), user.getAuthorities());
TokenInfo tokenInfo = jwtTokenProvider.generateToken(authentication);
tokenInfo.setEmail(user.getUserId());
tokenInfo.setMemberRole(user.getRole().toString());
return tokenInfo;
}
@Override
public void join(UserJoinDto userJoinDto) {
User user = new User();
user.setUserId(userJoinDto.getUserId());
user.setPassword(passwordEncoder.encode(userJoinDto.getPassword()));
user.setUserName(userJoinDto.getUserName());
userRepository.save(user);
}
@Override
public UserInfoDto info() {
UserInfoDto userInfoDto = SecurityUtil.getCurrentMemberId();
return userInfoDto;
}
@Override
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
return userRepository.findByUserId(userId)
.map(this::createUserDetails)
.orElseThrow(() -> new UsernameNotFoundException("해당하는 유저를 찾을 수 없습니다."));
}
private UserDetails createUserDetails(User user) {
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUsername())
.password(passwordEncoder.encode(user.getPassword()))
.roles(user.getRole().toString())
.build();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment