1. 로그인 구현
비밀번호가 일치하면 토큰을 생성해서 내려준다.
구현해야할 것
- 로그인 컨트롤러
- 로그인 서비스
- JwtUtils 클래스에 token 생성 로직
컨트롤러
@PostMapping("/login")
public ResponseEntity<SignIn.Response> signIn(
@RequestBody @Valid SignIn.Request request) {
return ResponseEntity.ok(memberService.signIn(request));
}
서비스
public SignIn.Response signIn(SignIn.Request request) {
Member member = getMemberByEmail(request.getEmail());
validatedPassword(request, member);
return SignIn.Response.toResponse(
jwtUtils.generateToken(request.getEmail(), "ATK")
, jwtUtils.generateToken(request.getEmail(), "RTK"));
}
JwtUtils
JWT 개념과 전체적인 스프링 시큐리티 인증과정 참고
스프링 핵심가이드) 북스터디 8주차 : 13장 서비스의 인증과 권한 부여 (tistory.com)
스프링 핵심가이드) 북스터디 8주차 : 13장 서비스의 인증과 권한 부여
13장 서비스의 인증과 권한 부여 13.1 보안 용어 이해 13.1.1 인증 - 인증(authentication)은 사용자가 누구인지 확인하는 단계 ex) 로그인 로그인은 DB에 등록된 아이디와 pw를 사용자가 입력한 아이디와 p
soni-developer.tistory.com
JWT는 Header, Payload, Signature 구조로 되어있다.
- Header
- kid : 서명시 사용하는 키
- typ : 토큰 유형
- alg : 서명 암호화 알고리즘
- Payload : 토큰에서 사용할 정보 조각들인 Claim이 담겨있음.
- iss (issue) : 토큰 발급자
- sub (subject) : 토큰 제목
- iat (issued at) : 토큰 발급 시간
- exp (expireation) : 토큰 만료 시간
- roles : 권한
여기서 roles는 public claims고 roles는 private claims다.
- signature : 헤더에서 정의한 알고리즘 방식 사용.
package com.sj.Petory.security;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
@RequiredArgsConstructor
@Slf4j
public class JwtUtils {
private static final String TOKEN_TYPE = "token_type";
private final UserDetailsService userDetailsService;
@Value("${spring.jwt.expiredAt.ATK}")
private String expiredAt_ATK;
@Value("${spring.jwt.expiredAt.RTK}")
private String expiredAt_RTK;
@Value("${spring.jwt.secret}")
private String secretKey;
public String generateToken(final String email, final String tokenType) {
Claims claims = Jwts.claims().setSubject(email);
claims.put(TOKEN_TYPE, tokenType);
Date now = new Date();
Date expiredDate = setExpired(tokenType, now);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(expiredDate)
.signWith(SignatureAlgorithm.HS512, secretKey)
.compact();
}
private Date setExpired(String tokenType, Date now) {
if (tokenType.equals("ATK")) {
return new Date(now.getTime() + Long.parseLong(expiredAt_ATK));
}
if (tokenType.equals("RTK")) {
return new Date(now.getTime() + Long.parseLong(expiredAt_RTK));
}
return null;
}
}
1. application.yml에 ATK, RTK 만료시간과 암호화에 사용할 SecretKey를 등록하였다.
2. JWT의 payload에 들어갈 Claims 객체에 subject로 email을 넣었다.
accesstoken과 refreshtoken을 구분하기 위해 token_type도 추가로 넣었다.
3. builder패턴으로 Jwt 토큰을 생성한다.
token 타입에 따른 만료시간을 다르게 설정해주기 위해 setExpired() 메소드를 추가로 구현하였다.
이렇게 구현해주면 로그인 시 accsstoken과 refreshtoken을 발급하여 응답으로 내려주게 된다.
Trouble
2024-08-14T01:00:08.319+09:00 ERROR 22424 --- [Petory] [nio-8080-exec-5] c.s.P.exception.GlobalExceptionHandler : jakarta.servlet.ServletException : Handler dispatch failed: java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter
원인 : JAXB API가 java11에서부터는 완전히 제거되어 의존성을 추가로 넣어주어야 한다.
해결 :
https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api/2.3.1에서 javax.xml.bind 검색 후 의존성 추가
implementation 'javax.xml.bind:jaxb-api:2.3.1'
2. 인증이 필요한 API 구현
회원 정보를 조회하는 페이지에 들어가기 위해서는 로그인 한 회원 정보가 필요하다.
직접 컨트롤러 파라미터로 요청 헤더에 담겨오는 토큰을 받아서 파싱해도 되지만 @AuthenticationPrincipal을 사용해서 로그인한 Authentication 객체를 가져오기로 했다.
@AuthenticationPrincipal에 대한 정보는 아래 포스팅으로 확인
GiftFunding) 헤더에 JWT 토큰으로 로그인 후 사용자 정보 얻기 (tistory.com)
GiftFunding) 헤더에 JWT 토큰으로 로그인 후 사용자 정보 얻기
프로젝트에 로그인 기능을 구현하고 추후 권한이 필요한 리소스에 접근할 때 헤더에 입력된 토큰을 컨트롤러에서 @ReqeustHeader로 받아와서 서비스 단에 넘겨준 후 토큰에서 claims를 파싱해서 subjec
soni-developer.tistory.com
해당 API를 구현하기 위해서는 아래의 것들을 만들어줘야 한다.
- 회원정보 조회 컨트롤러
- 회원정보 조회 서비스
- JwtUtils에 받아올 토큰을 파싱하는 로직
- Filter를 이용해 요청이 들어올 때마다 헤더에 토큰을 파싱해서 검증해주는 로직
- @AutenticationPrincipal은 스프링 시큐리티의 인증이 완료된 객체인 UserDetils를 가지고 있으므로 이를 상속받아 필요한 정보만 뽑아올 MemberAdapter dto
- UserDetails 객체를 뽑아오기 위해 UserDetailsService의 loadByUser() 메소드를 사용해야한다. 이를 위해 memberService가 UserDetailsService를 implements해서 loadByUser()메소드를 구현해야 함.
컨트롤러
로그인 한 사용자 정보를 받아오기 위해서는 UserDetails를 상속받을 dto 객체에 @AuthenticationPrincipal을 붙이면 된다.
@GetMapping
public ResponseEntity<MemberInfoResponse> getMembers(
@AuthenticationPrincipal MemberAdapter memberAdapter) {
return ResponseEntity.ok(memberService.getMembers(memberAdapter));
}
서비스
public MemberInfoResponse getMembers(final MemberAdapter memberAdapter) {
Member member = getMemberByEmail(memberAdapter.getEmail());
return MemberInfoResponse.fromEntity(member);
}
MemberInfoResponse
package com.sj.Petory.domain.member.dto;
import com.sj.Petory.domain.member.entity.Member;
import lombok.*;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MemberInfoResponse {
private String name;
private String phone;
private String image;
public static MemberInfoResponse fromEntity(Member member) {
return MemberInfoResponse.builder()
.name(member.getName())
.phone(member.getPhone())
.build();
}
}
이렇게만 만들어주면 SecurityContext에 Authentication이 없어서 @AuthenticationPrincipal로 MemberAdapter 객체를 받아오지 못 한다. 따라서 다음과 같은 오류가 난다.
정상적인 인증 과정을 거치기 위해 추가해야할 작업은 다음과 같다.
JwtAuthenticationFilter
- OncePerRequestFilter를 상속받아 매 요청시마다 한 번씩 필터를 거치도록 한다.
- HttpServletRequest header에서 토큰을 가져온다. (JwtUtils.resolveToken())
- 가져온 토큰이 유효한지 확인한다.(JwtUtils.validateToken())
- 토큰을 통해 Authentication 객체를 얻는다. (JwtUtils.getAuthentication())
- 가져온 Authentication을SecurityContextHolder에 추가한다.
JwtUtils
- resolveToken() : HttpServletRequest Header에서 토큰을 뽑아오는 메소드
- validateToken() : 토큰의 만료시간을 검증하여 토큰이 유효한지 확인하는 메소드
- getAuthentication() : Authentication을 만드는 메소드. 여기서 UserDetailsService의 loadUserByUsername()이 사용됨
- parseClaims() : jwt토큰에서 claims를 파싱하는 메소드
JwtAuthenticationFilter
package com.sj.Petory.security;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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;
@Component
@RequiredArgsConstructor
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtils jwtUtils;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = jwtUtils.resolveToken(request);
if (StringUtils.hasText(token) && jwtUtils.validateToken(token)) {
Authentication authentication = jwtUtils.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
1. jwtUtils.resolveToken(request)
: 헤더를 메소드의 파라미터로 보내 token값을 얻는다.
2. if 조건문
: 토큰 값이 있고 validateToken() 메소드를 통해 만료되지 않았으면 안의 코드를 실행
3. Authentication authentication = jwtUtils.getAuthentication(token);
: getAuthentication() 메소드를 통해 Authentication 객체를 얻어온다.
4. 3에서 얻어온 Authentication 객체를 SecurityContextHolder에 저장한다.
JwtUtils
public String resolveToken(HttpServletRequest request) {
String header = request.getHeader("Authorization");
if (!StringUtils.hasText(header) || !header.startsWith(TOKEN_PREFIX)) {
return null;
}
return header.substring(TOKEN_PREFIX.length());
}
resolveToken() : Http 요청 헤더에서 Authorization으로 들어온 값을 Token prefix를 빼고 반환합니다.
public boolean validateToken(String token) {
Date exp = parseClaims(token).getExpiration();
if (exp.before(new Date())) {//날짜 개념 학습
throw new MemberException(ErrorCode.TOKEN_EXPIRED);
}
return true;
}
validateToken() : Jwts.parser()를 이용해 토큰을 파싱한다.
.setSigningKey()로 토큰 생성할 때 넣어줬던 secretKey를 넣어줘야하고
.parseClaimsJws(token)으로 토큰의 Claims를 파싱해준다.
거기서 .getBody().getExpiration()으로 만료일자를 가져와 조건문으로 비교해준다.
parseClaims()메소드는 아래 코드에 있다.
public Authentication getAuthentication(String token) {
UserDetails userDetails = userDetailsService.loadUserByUsername(
parseEmail(token));
return new UsernamePasswordAuthenticationToken(
userDetails
, ""
, userDetails.getAuthorities()
);
}
private String parseEmail(String token) {
return parseClaims(token).getSubject();
}
private Claims parseClaims(String token) {
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token).getBody();
}
getAuthentication() : UserDetailsService의 loadUserByUsername() 메소드를 통해 UserDetails 객체를 받아오고
Authentication을 반환한다.
Authentication 객체를 만들 때 UsernamePasswordAuthenticationToken(principal, credentials, authorities)을 넣어준다.
여기서 pricipal에는 UserDetails객체를, authorities에는 userDetails.getAuthorities()로
권한 정보를 넣어줘야한다.
참고로 userDetails.getAuthorities를 안 넣어주면 인증 객체가 생성되어도 Autheticated = false로 뜨며 api 요청 시 forbidden이 뜨게 된다.
parseCliams는token에서 Claims를 가져오는 메소드이고 parseEmail은 토큰에서 subject(토큰 생성 시 subject로 토큰을 저장해놓음) 를 가져오는 메소드이다.
Trouble
2024-08-15T19:15:36.953+09:00 INFO 25608 --- [Petory] [nio-8080-exec-4] c.s.P.security.JwtAuthenticationFilter : context 잘 됐니 ?UsernamePasswordAuthenticationToken [Principal=com.sj.Petory.domain.member.dto.MemberAdapter@343c5163, Credentials=[PROTECTED], Authenticated=false, Details=null, Granted Authorities=[]]
해당 로그와 함께 인증 필요한 API 요청 시 403 Forbidden 에러 발생
UsernamePasswordAuthenticationToken 생성자는 세 번째 매개변수로 권한 목록을 받으며, 이를 통해 인증된 사용자로 설정 된다.
해결 : getAuthentication()에 new UsernamePasswordAuthenticationToken()의 세 번째 인자로 userDetails.getAuthorities()를 넣어준다.
코드 전문
package com.sj.Petory.security;
import com.sj.Petory.domain.member.dto.MemberAdapter;
import com.sj.Petory.domain.member.service.UserDetailsServiceImpl;
import com.sj.Petory.exception.MemberException;
import com.sj.Petory.exception.type.ErrorCode;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
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.stereotype.Component;
import java.util.Date;
@Component
@RequiredArgsConstructor
@Slf4j
public class JwtUtils {
private static final String TOKEN_TYPE = "token_type";
private final UserDetailsServiceImpl userDetailsService;
@Value("${spring.jwt.expiredAt.ATK}")
private String expiredAt_ATK;
@Value("${spring.jwt.expiredAt.RTK}")
private String expiredAt_RTK;
@Value("${spring.jwt.secret}")
private String secretKey;
public String generateToken(final String email, final String tokenType) {
Claims claims = Jwts.claims().setSubject(email);
claims.put(TOKEN_TYPE, tokenType);
Date now = new Date();
Date expiredDate = setExpired(tokenType, now);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(expiredDate)
.signWith(SignatureAlgorithm.HS512, secretKey)
.compact();
}
private Date setExpired(String tokenType, Date now) {
if (tokenType.equals("ATK")) {
return new Date(now.getTime() + Long.parseLong(expiredAt_ATK));
}
if (tokenType.equals("RTK")) {
return new Date(now.getTime() + Long.parseLong(expiredAt_RTK));
}
return null;
}
public String resolveToken(HttpServletRequest request) {
String header = request.getHeader("Authorization");
if (header == null || !header.startsWith("Bearer ")) {
return null;
}
return header.substring("Bearer ".length());
}
public boolean validateToken(String token) {
Date exp = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody()
.getExpiration();
if (exp.before(new Date())) {//날짜 개념 학습
throw new MemberException(ErrorCode.TOKEN_EXPIRED);
}
return true;
}
public Authentication getAuthentication(String token) {
UserDetails userDetails = userDetailsService.loadUserByUsername(
parseEmail(token));
return new UsernamePasswordAuthenticationToken(
userDetails
, ""
, userDetails.getAuthorities()
);
}
private String parseEmail(String token) {
return parseClaims(token).getSubject();
}
private Claims parseClaims(String token) {
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token).getBody();
}
}
MemberAdapter
UserDetails를 인증 객체로 사용할 것이기 때문에 이를 상속한 dto가 필요하다.
entity에 UserDetails를 받아오는 것은 영속성 문제로 좋지 않으니 꼭 dto를 만들어 상속받도록 하자.
GiftFunding) 헤더에 JWT 토큰으로 로그인 후 사용자 정보 얻기 (tistory.com)
GiftFunding) 헤더에 JWT 토큰으로 로그인 후 사용자 정보 얻기
프로젝트에 로그인 기능을 구현하고 추후 권한이 필요한 리소스에 접근할 때 헤더에 입력된 토큰을 컨트롤러에서 @ReqeustHeader로 받아와서 서비스 단에 넘겨준 후 토큰에서 claims를 파싱해서 subjec
soni-developer.tistory.com
package com.sj.Petory.domain.member.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
@Getter
@RequiredArgsConstructor
@Builder
public class MemberAdapter implements UserDetails{
private final String email;
private final String password;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.email;
}
@Override
public boolean isAccountNonExpired() {
return UserDetails.super.isAccountNonExpired();
}
@Override
public boolean isAccountNonLocked() {
return UserDetails.super.isAccountNonLocked();
}
@Override
public boolean isCredentialsNonExpired() {
return UserDetails.super.isCredentialsNonExpired();
}
@Override
public boolean isEnabled() {
return UserDetails.super.isEnabled();
}
}
UserDetailsServiceImpl
UserDetailsService인터페이스를 상속받을 클래스를 생성한다.
loadByUsername() 메소드를 구현하여 위에서 만든 MemberAdapter 객체를 반환하도록 해야한다.
package com.sj.Petory.domain.member.service;
import com.sj.Petory.domain.member.dto.MemberAdapter;
import com.sj.Petory.domain.member.entity.Member;
import com.sj.Petory.domain.member.repository.MemberRepository;
import com.sj.Petory.exception.MemberException;
import com.sj.Petory.exception.type.ErrorCode;
import lombok.RequiredArgsConstructor;
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;
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final MemberRepository memberRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Member member = memberRepository.findByEmail(email)
.orElseThrow(() -> new MemberException(ErrorCode.MEMBER_NOT_FOUND));
return new MemberAdapter(member.getEmail(), member.getPassword());
}
}
loadUserByUsername 메소드는 DB에서 인증된 객체인 UserDetails를 반환하는데 MemberAdapter는 위에서 UserDetails를 상속받았으므로 반환에 꼭 MemberAdapter를 사용한다.
(이래야 나중에 컨트롤러에서 @AuthenticationPrincipal로 로그인 한 사용자 정보를 받아올 때 사용할 수 있다.)
SeurityConfig
package com.sj.Petory.config;
import com.sj.Petory.security.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.headers(headers ->
headers
.frameOptions(
HeadersConfigurer.FrameOptionsConfig::sameOrigin)
)
.authorizeHttpRequests((authz) ->
authz
.requestMatchers(HttpMethod.GET, "/members").authenticated()
.requestMatchers(HttpMethod.POST, "/members").permitAll()
.requestMatchers(
"/members/check-email"
, "/members/check-name"
, "members/login"
, "/h2-console/**"
, "/docs/**", "/v3/api-docs/**", "/swagger-ui/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); // JwtAuthenticationFilter를 UsernamePasswordAuthenticationFilter 전에 추가
return http.build();
}
}
제일 마지막에 addFilterBefore가 없으면 403 forbidden 에러가 나게 된다.
해당 코드는 커스텀으로 생성한 JwtAuthenticationFilter를 Spring Security의 필터 체인에서 실행되도록 하는 것이다.
이 설정이 없으면 JwtAuthenticationFilter가 Spring Security의 필터 체인에서 실행되지 않아 JWT 인증/검증 과정이 이루어지지 않는다.
JwtAuthenticationFilter는 인증을 담당하는 필터이기 때문에 UsernamePasswordAuthenticationFilter 전에 실행되어야 한다.
UsernamePasswordAuthenticationFilter는 기본적으로 폼 기반 로그인 처리에 사용되는 필터이다.
따라서 JWT를 기반으로 한 인증을 먼저 처리하기 위해 JwtAuthenticationFilter가 UsernamePasswordAuthenticationFilter보다 먼저 실행되도록 설정한다.
위와 같이 코드를 작성하면
이렇게 인증이 필요한 객체에서 Authorization 으로 Bearer이 붙은 jwtToken을 보내줬을 때 정상적인 인증이 가능해진다 !
이번에 로그인 및 인증을 구현하면서 JWT의 구조와 생성, 파싱하는 것을 더 습득한 것 같다.
특히 Spring Security 인증 과정이 어렵기만 했는데 습득된 거 같아서 매우 뿌듯하다 !!! 😁😁
참고
HandleDispatch fail 에러 해결
https://devthriver.tistory.com/13
[오류해결] jakarta.servlet.ServletException: Handler dispatch failed: java.lang.NoClassDefFoundError: javax/xml/bind/Datatyp
유튜브 서타몽님의 https://www.youtube.com/@jiraynorprogramming1589 서타몽 www.youtube.com SpringBoot + Reactjs(ts) + MySQL] 로그인 API 구현을 따라하면서 마지막 PostMan으로 로그인을 했을시 아래와 같은 에러가 뜬다.
devthriver.tistory.com
스프링 시큐리티 설정
[Spring] Spring Security를 이용한 로그인 구현 (스프링부트 3.X 버전) [1] - 동작 원리 및 Config 설정
스프링 부트 3.0 이상 버전의 시큐리티 사용법 및 바뀐 Config 작성법을 다루고 있습니다.
velog.io
스프링 시큐리티 기본 개념과 구조
[Spring]Security - 스프링시큐리티의 기본 개념과 구조
이번시간에는 스프링 시큐리티를 사용하는 법을 공부해보려고 합니다. 스프링 시큐리티의 로그인 진행방식을 먼저 살펴보겠습니다.우선 스프링 시큐리티를 설정을 통해, 기본적인 접근 제어와
velog.io
JWT 생성 참고
JWT 생성 및 인증
JWT를 발급받고 인증해보자!JWT란?JWT는 Json Web Token으로, 인증에 필요한 정보들을 토큰에 담아 암호화시켜 사용하는 방식이다. 서명된 토큰이라는 점에서 쿠키보다 더 보안적이라고 할 수 있다.JWT
velog.io
Security Security Config
[Spring Security] Spring Security Filter Chain 에 대해
안녕하세요 오늘은 Spring Security 에서 인증,인가 과정을 담당하는 Security Filter Chain 에 대해 알아보겠습니다.Security Filter Chain 이란 Spring Security 에서 제공하는 인증,인가를 위한 필터들의 모음입니
velog.io
시큐리티 JWT 구성
스프링 시큐리티 JWT 구현 해보기
목차 JWT 인증방식 동작 원리 SecurityConfig 추가, DB연결, 회원가입 추가 회원가입 구현, AuthenticationFilter, AuthenticationManager 구현 DB 저장 구현, UserDetailService, UserDetail 구현
velog.io
'공부 > Spring Boot' 카테고리의 다른 글
SpringBoot에 Flyway적용해서 DB 형상 관리 하기 + JPA 옵션, flyway 옵션 정리 (0) | 2024.08.26 |
---|---|
Spring Boot) 백 & 프론트 카카오 로그인 API 구현하기 (4) | 2024.08.24 |
Spring Boot 3.x 버전에서 Spring Security 적용기 (2) | 2024.08.13 |
Spring Boot) 회원가입 시 PasswordEncoder 이용해 비밀번호 암호화 (0) | 2024.08.12 |
SpringBoot 3.x 버전에서 RestDocs + SwaggerUI 사용하기 (0) | 2024.08.01 |