inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

묻고 답해요

173만명의 커뮤니티!! 함께 토론해봐요.

[급함]로그인시 jwt 발급 문제

미해결

스프링부트 시큐리티 & JWT 강의

https://github.com/YuYoHan/project_study1 전체 코드 질문 1) // 로그인 @PostMapping("/api/v1/users/login") public ResponseEntity<?> login(@RequestBody MemberDTO memberDTO) throws Exception { log.info("member : " + memberDTO); try { log.info("-----------------"); ResponseEntity<TokenDTO> login = memberService.login(memberDTO.getUserEmail(), memberDTO.getUserPw()); log.info("login : " + login); return ResponseEntity.ok().body(login); } catch (Exception e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("문제가 있습니다"); } } // 로그인 public ResponseEntity<TokenDTO> login(String userEmail, String userPw) throws Exception { // Login ID/PW를 기반으로 UsernamePasswordAuthenticationToken 생성 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userEmail, userPw); log.info("----------------------"); log.info("authenticationToken : " +authenticationToken); log.info("----------------------"); // 실제 검증(사용자 비밀번호 체크)이 이루어지는 부분 // authenticateToken을 이용해서 Authentication 객체를 생성하고 // authentication 메서드가 실행될 때 // CustomUserDetailsService에서 만든 loadUserbyUsername 메서드가 실행 Authentication authentication = authenticationManagerBuilder .getObject().authenticate(authenticationToken); log.info("----------------------"); log.info("authentication : " + authentication); log.info("----------------------"); // 해당 객체를 SecurityContextHolder에 저장 SecurityContextHolder.getContext().setAuthentication(authentication); // authentication 객체를 createToken 메소드를 통해서 생성 // 인증 정보를 기반으로 생성 TokenDTO tokenDTO = jwtProvider.createToken(authentication); log.info("----------------------"); log.info("tokenDTO : " + tokenDTO); log.info("----------------------"); HttpHeaders headers = new HttpHeaders(); // response header에 jwt token을 넣어줌 headers.add(JwtAuthenticationFilter.HEADER_AUTHORIZATION, "Bearer " + tokenDTO); log.info("----------------------"); log.info("headers : " + headers); log.info("----------------------"); MemberEntity member = memberRepository.findByUserEmail(userEmail); log.info("member : " + member); TokenEntity tokenEntity = TokenEntity.builder() .grantType(tokenDTO.getGrantType()) .accessToken(tokenDTO.getAccessToken()) .refreshToken(tokenDTO.getRefreshToken()) .userEmail(tokenDTO.getUserEmail()) .nickName(member.getNickName()) .userId(member.getUserId()) .build(); log.info("token : " + tokenEntity); tokenRepository.save(tokenEntity); return new ResponseEntity<>(tokenDTO, headers, HttpStatus.OK); } package com.example.project1.config.auth; import com.example.project1.entity.member.MemberEntity; import lombok.Getter; import lombok.Setter; import lombok.ToString; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.oauth2.core.user.OAuth2User; import java.util.ArrayList; import java.util.Collection; import java.util.Map; @Setter @Getter @ToString public class PrincipalDetails implements UserDetails, OAuth2User { private MemberEntity member; private Map<String, Object> attributes; // 일반 로그인 public PrincipalDetails(MemberEntity member) { this.member = member; } // OAuth2 로그인 public PrincipalDetails(MemberEntity member, Map<String, Object> attributes) { this.member = member; this.attributes = attributes; } // 해당 유저의 권한을 리턴하는 곳 @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> collection = new ArrayList<>(); collection.add(new GrantedAuthority() { @Override public String getAuthority() { return "ROLE_" + member.getUserType().toString(); } }); return collection; } // 사용자 패스워드를 반환 @Override public String getPassword() { return member.getUserPw(); } // 사용자 이름 반환 @Override public String getUsername() { return member.getUserEmail(); } // 계정 만료 여부 반환 @Override public boolean isAccountNonExpired() { // 만료되었는지 확인하는 로직 // true = 만료되지 않음 return true; } // 계정 잠금 여부 반환 @Override public boolean isAccountNonLocked() { // true = 잠금되지 않음 return true; } // 패스워드의 만료 여부 반환 @Override public boolean isCredentialsNonExpired() { // 패스워드가 만료되었는지 확인하는 로직 // true = 만료되지 않음 return true; } // 계정 사용 가능 여부 반환 @Override public boolean isEnabled() { // 계정이 사용 가능한지 확인하는 로직 // true = 사용 가능 return true; } @Override public Map<String, Object> getAttributes() { return attributes; } @Override public String getName() { return null; } } @Service @RequiredArgsConstructor @Slf4j public class PrincipalDetailsService implements UserDetailsService { private MemberRepository memberRepository; // 시큐리티 session = Authentication = UserDetails // 함수 종료시 @AuthenticationPrincipal 어노테이션이 만들어진다. @Override public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException { MemberEntity member = memberRepository.findByUserEmail(userEmail); log.info("user : " + member); return new PrincipalDetails(member); } } package com.example.project1.config.jwt; import com.example.project1.domain.jwt.TokenDTO; import io.jsonwebtoken.*; 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.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.stereotype.Component; import io.jsonwebtoken.security.Keys; import javax.xml.bind.DatatypeConverter; import java.security.Key; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.stream.Collectors; @Slf4j @Component public class JwtProvider { private static final String AUTHORITIES_KEY = "auth"; @Value("${jwt.access.expiration}") private long accessTokenTime; @Value("${jwt.refresh.expiration}") private long refreshTokenTime; private Key key; public JwtProvider( @Value("${jwt.secret_key}") String secret_key) { byte[] secretByteKey = DatatypeConverter.parseBase64Binary(secret_key); this.key = Keys.hmacShaKeyFor(secretByteKey); } // 유저 정보를 가지고 AccessToken, RefreshToken 을 생성하는 메소드 public TokenDTO createToken(Authentication authentication) { // 권한 가져오기 String authorities = authentication.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.joining(",")); long now = (new Date()).getTime(); Date now2 = new Date(); // AccessToken 생성 Date accessTokenExpire = new Date(now + this.accessTokenTime); String accessToken = Jwts.builder() // 내용 sub : 유저의 이메일 // 토큰 제목 .setSubject(authentication.getName()) // 클레임 id : 유저 ID .claim(AUTHORITIES_KEY, authorities) // 내용 exp : 토큰 만료 시간, 시간은 NumericDate 형식(예: 1480849143370)으로 하며 // 항상 현재 시간 이후로 설정합니다. .setExpiration(accessTokenExpire) // 서명 : 비밀값과 함께 해시값을 ES256 방식으로 암호화 .signWith(key, SignatureAlgorithm.HS256) .compact(); log.info("accessToken : " + accessToken); // RefreshToken 생성 Date refreshTokenExpire = new Date(now + this.refreshTokenTime); String refreshToken = Jwts.builder() .setSubject(authentication.getName()) .claim(AUTHORITIES_KEY, authorities) .setExpiration(refreshTokenExpire) .signWith(key, SignatureAlgorithm.HS256) .compact(); log.info("refreshToken : " + refreshToken); return TokenDTO.builder() .grantType("Bearer") .accessToken(accessToken) .refreshToken(refreshToken) // principalDeatails에서 getUserName 메소드가 반환한 것을 담아준다. // 이메일을 반환하도록 구성했으니 이메일이 반환됩니다. .userEmail(authentication.getName()) .build(); } // accessToken 생성 public TokenDTO createAccessToken(String userEmail) { Long now = (new Date()).getTime(); Date now2 = new Date(); Date accessTokenExpire = new Date(now + this.accessTokenTime); String accessToken = Jwts.builder() .setIssuedAt(now2) .setSubject(userEmail) .setExpiration(accessTokenExpire) .signWith(key, SignatureAlgorithm.HS256) .compact(); log.info("accessToken : " + accessToken); return TokenDTO.builder() .grantType("Bearer ") .accessToken(accessToken) .userEmail(userEmail) .build(); } // JWT 토큰을 복호화하여 토큰에 들어있는 정보를 꺼내는 코드 // 토큰으로 클레임을 만들고 이를 이용해 유저 객체를 만들어서 최종적으로 authentication 객체를 리턴 // 인증 정보 조회 public Authentication getAuthentication(String token) { // 토큰 복호화 메소드 Claims claims = parseClaims(token); if(claims.get("auth") == null) { throw new RuntimeException("권한 정보가 없는 토큰입니다."); } // 클레임 권한 정보 가져오기 Collection<? extends GrantedAuthority> authorities = Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); // UserDetails 객체를 만들어서 Authentication 리턴 User principal = new User(claims.getSubject(), "", authorities); return new UsernamePasswordAuthenticationToken(principal, token, authorities); } private Claims parseClaims(String token) { try { return Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(token) .getBody(); } catch (ExpiredJwtException e) { log.info("ExpiredJwtException : " + e.getMessage()); log.info("ExpiredJwtException : " + e.getClaims()); return e.getClaims(); } } // 토큰의 유효성 검증을 수행 public boolean validateToken(String token) { try { Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(token); return true; } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) { log.info("잘못된 JWT 서명입니다."); } catch (ExpiredJwtException e) { log.info("만료된 JWT 토큰입니다."); } catch (UnsupportedJwtException e) { log.info("지원되지 않는 JWT 토큰입니다."); }catch (IllegalArgumentException e) { log.info("JWT 토큰이 잘못되었습니다."); } return false; } } @RequiredArgsConstructor @Slf4j public class JwtAuthenticationFilter extends GenericFilterBean { public static final String HEADER_AUTHORIZATION = "Authorization"; private final JwtProvider jwtProvider; // doFilter는 토큰의 인증정보를 SecurityContext에 저장하는 역할 수행 @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; // Request Header에서 JWT 토큰을 추출 String jwt = resolveToken(httpServletRequest); String requestURI = httpServletRequest.getRequestURI(); if(StringUtils.hasText(jwt) && jwtProvider.validateToken(jwt)){ // 토큰이 유효할 경우 토큰에서 Authentication 객체를 가지고 와서 SecurityContext에 저장 Authentication authentication = jwtProvider.getAuthentication(jwt); SecurityContextHolder.getContext().setAuthentication(authentication); log.info("Security Context에 '{}' 인증 정보를 저장했습니다., uri : {}", authentication.getName(), requestURI); } else { log.debug("유효한 JWT 토큰이 없습니다. uri : {}", requestURI); } chain.doFilter(request, response); } // Request Header 에서 토큰 정보를 꺼내오기 위한 메소드 private String resolveToken(HttpServletRequest httpServletRequest) { String bearerToken = httpServletRequest.getHeader(HEADER_AUTHORIZATION); if(StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } else { return null; } } } 대략 적인 코드는 다음과 같습니다. 근데 컨트롤러에서 로그인 시 Exception에 걸려서 문제가 있다고 문구 찍은게 나오네요. log 돌려보니까 service에서 authenticationToken객체는 나오는데 authentication 여기서 부터 안나오는거 보니 여기서 문제가 있는거 같은데 400번 bad Request가 뜹니다 ㅠㅠ 질문2) 현재 방법이 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userEmail, userPw); 을 통해서 authentication으로 token을 생성하고 있는데 그냥 userEmail로만 받고 // accessToken 생성 public TokenDTO createAccessToken(String userEmail) { Long now = (new Date()).getTime(); Date now2 = new Date(); Date accessTokenExpire = new Date(now + this.accessTokenTime); String accessToken = Jwts.builder() .setIssuedAt(now2) .setSubject(userEmail) .setExpiration(accessTokenExpire) .signWith(key, SignatureAlgorithm.HS256) .compact(); log.info("accessToken : " + accessToken); return TokenDTO.builder() .grantType("Bearer ") .accessToken(accessToken) .userEmail(userEmail) .build(); } 이런식으로 토큰을 생성해도 괜찮나요? 질문3) JwtAuthenticationFilter 클래스에서 @RequiredArgsConstructor @Slf4j public class JwtAuthenticationFilter extends GenericFilterBean { public static final String HEADER_AUTHORIZATION = "Authorization"; private final JwtProvider jwtProvider; // doFilter는 토큰의 인증정보를 SecurityContext에 저장하는 역할 수행 @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; // Request Header에서 JWT 토큰을 추출 String jwt = resolveToken(httpServletRequest); String requestURI = httpServletRequest.getRequestURI(); if(StringUtils.hasText(jwt) && jwtProvider.validateToken(jwt)){ // 토큰이 유효할 경우 토큰에서 Authentication 객체를 가지고 와서 SecurityContext에 저장 Authentication authentication = jwtProvider.getAuthentication(jwt); SecurityContextHolder.getContext().setAuthentication(authentication); log.info("Security Context에 '{}' 인증 정보를 저장했습니다., uri : {}", authentication.getName(), requestURI); } else { log.debug("유효한 JWT 토큰이 없습니다. uri : {}", requestURI); } chain.doFilter(request, response); } // Request Header 에서 토큰 정보를 꺼내오기 위한 메소드 private String resolveToken(HttpServletRequest httpServletRequest) { String bearerToken = httpServletRequest.getHeader(HEADER_AUTHORIZATION); if(StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } else { return null; } } } 이 처리를 해줬으니 만약 access token이 만료되서 refresh token을 보내서 access token을 발급받으려고 할 때 Bearer가 있는지 확인을 더 해줄 필요 없이 여기서 처리하니 바로 header에 담겨온 refresh token을 빼와서 유효성 검사를 해주고 access token을 발급해주면 되나요?

  • spring
  • spring-security
  • jwt
유요한 댓글 1 좋아요 0 조회수 865

페이스북 로그인이 안 됩니다.

해결됨

스프링부트 시큐리티 & JWT 강의

시큐리티 10강 페이스북 로그인을 하려고 하면 WARN 24016 --- [nio-8080-exec-2] o.a.c.util.SessionIdGeneratorBase : Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [231] milliseconds. 이런 에러가 발생합니다. 이 에러가 발생하기 전에 2. JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning 이 에러가 먼저 발생해서 application.yml에 jpa: open-in-view: false이 코드를 추가해서 고쳤습니다. 2번을 고치고 나서 다시 실행해보니 1번 에러가 발생했는데 어떻게 고쳐야할지 모르겠습니다.

  • spring
  • spring-security
  • jwt
Lego 댓글 1 좋아요 0 조회수 392

findByUsername(username)을 사용하는 이유가 궁금합니다.

미해결

스프링부트 시큐리티 & JWT 강의

안녕하세요 강사님 궁금한 점이 해결되지 않아서 질문 남깁니다. 강사님 git에 있는 소스를 그대로 갖고 왔습니다. 토큰이 정상적인 상황일 때, 즉 username 값이 존재할 때 findByUsername을 사용해서 user객체를 얻는 장면인데 토큰 안에 User객체 자체를 저장해서 불필요한 DB연동을 안 하는 방식은 잘못된 방식인가요? 제 생각에는 이렇게 사용하면 토큰에 User 정보가 노출되기 때문에 보안이 취약하다 라고 생각하는데 이 생각이 맞는지 궁금합니다. username은 충분히 겹칠 수 있는 값이라고 생각이 드는데 토큰을 저장할 때 Id값을 저장해서 꺼내오는 방식은 잘못된 방식일까요?

  • spring
  • spring-security
  • jwt
진짜 잘하고싶다 댓글 1 좋아요 0 조회수 489

key 보관 방법

미해결

스프링부트 시큐리티 & JWT 강의

수업 듣다가 궁금증이 생겨서 남깁니다. key 생성 시에 현재 실습에서는 "cos" 를 사용했는데, 실제로 사용할때는 key를 어떻게 생성하고, 보관해야하는지 궁금합니다.

  • spring
  • spring-security
  • jwt
공부해야지 댓글 1 좋아요 0 조회수 268

ejs, cjs 둘 중 무엇으로 코딩해야하나요?

미해결

[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지

ejs와 cjs 둘 중 express 프레임워크를 사용해서 프로그래밍을 하려면 어떤 방식을 추천하시나요?

  • node.js
  • mysql
  • mongodb
  • express
  • typescript
  • socket.io
  • jwt
박동규 댓글 1 좋아요 0 조회수 544

PDF파일이 어디에 있나요?

해결됨

Python 알고리즘 베스트 10

안녕하세요 PDF파일은 출력하려고 하는데, 노션을 보면 "PDF 노션 페이지에서 다운로드하실 수 있습니다." 로만 되어있는데 다운받을 수 있는 링크는 어디에 있나요? 노션에서 전체PDF출력하려면 비지니스라서 불가능한데... 확인부탁드립니다.

  • python
  • 코딩-테스트
한형섭 댓글 2 좋아요 0 조회수 487

UserDetails 안에 dto

미해결

스프링부트 시큐리티 & JWT 강의

안녕하세요. 좋은 강의 만들어주셔서 감사합니다. 궁금한 점이 생겨서 질문드립니다. 강의에서는 UserDetails를 구현할 때 내부에 엔티티 자체를 넣어주셨는데 엔티티 자체를 넣는게 엔티티를 dto로 변환 후 넣는 것보다 좋나요?

  • spring
  • spring-security
  • jwt
heehee 댓글 2 좋아요 0 조회수 455

admin 에 나타나지 않는 몇몇 필드들

미해결

파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트

안녕하세요, 강의를 잘 듣고 있습니다. 모델 필드에 있는 몇몇 필드들이 admin에 나타나지 않더군요 예를 들면, updated_at, created_at 같은 필드들이요 이를 위해서 admin 페이지에 일일히 모델 필드를 list_display에 등록해줘야 하는게 맞나요? from django.contrib import admin # Register your models here. from .models import * admin.site .empty_value_display = "-empty-" admin.site .register(Product) admin.site .register(CartProduct) class OrderAdmin(admin.ModelAdmin): list_display = ['customer', 'transaction_id', 'total_price'] admin.site .register(Category) admin.site .register(UserProfile) admin.site .register(Order) admin.site .register(OrderedProduct) admin.site .register(ShipmentInfo) 그럼 제가 직접만든 모델의 경우에는 그렇다 쳐도.. allauth에 있는 site domain 부분이 나오질 않는거에요 ㅠㅠ... 제가 뭘 잘못 건드렸는 지 모르겠는데, 맨처음 프로젝트할 때에는 allauth의 소셜 어플리케이션 부분에 사이트 도메인을 입력할 수 있는 커다란 박스가 있었는데, 그것만 또 안난옵니다. 제가 뭘 잘못한건지 ㅠㅠ 원래 잘 나오던건데... 이번에 파이참 커뮤니티 에디션에서 유료버전으로 바꾸고, 프로젝트를 만들고 나니 admin에 몇몇 모델의 필드들이 잘 보이지 않습니다. verbose name을 설정된것들이 특히 그런 거 같은데 무엇이 문제인지 도통 모르겠습니다. 그렇다고 allauth를 제가 admin에 등록해야하는걸까요? 2.제가 모르는 무언가가 있는걸까요?

  • react
  • python
  • django
  • docker
paichai17 댓글 1 좋아요 0 조회수 307

부하테스트중 errors.ETIMEDOUT:

해결됨

[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지

안녕하세요 json부하테스트중에 에러표시나서 방화벽들어가서 mysql포트 확인하고 3306설정했는데 안되서 포트80도해보고 했는데 에러표시만뜹니다 https://binshuuuu.tistory.com/m/214 제가 따라한 설정입니다

  • node.js
  • mysql
  • mongodb
  • express
  • typescript
  • socket.io
  • jwt
Hongsun1 댓글 1 좋아요 0 조회수 357

선생님 premium 해결했습니다

해결됨

[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지

선생님 콘솔로그 쳐서 premium위치 찾아서req.user.Domains [5].type === 'premium' 했는데 true 나와서 해결했습니다 감사합니다 앞으로 콘솔로그 쳐서 하는 습관가져야겠습니다 정말 감사합니다 ^_^!!!

  • node.js
  • mysql
  • mongodb
  • express
  • typescript
  • socket.io
  • jwt
Hongsun1 댓글 0 좋아요 1 조회수 299

ansible-server에 pywinrm 설치 시, 에러 발생하여 문의드립니다.

미해결

구성 관리 자동화 도구 - 앤서블(Ansible)

안녕하세요. 아래 섹션 실습 중, 에러가 발생하여 문의드립니다. 섹션 9 : [응용] 윈도우 관리학 - (1)베이그런트를 이용해서 윈도우를 추가하기 Ansible_env_ready.yaml 에 아래와 같이 추가 후, vagrant provision ansible-server을 수행했는데 pvwinrm 설치 과정에서 에러가 발생하였습니다. - name: Install python-pip yum: name: python-pip state: present - name: Install pywinrm pip: name: pywinrm state: present ansible-server에 접속하여 수동으로 pip install pywinrm을 수행했는데 역시 에러가 발생합니다. python 버전을 업그레이드 하라고 메시지가 나오는데 향후 수업 따라하기 시, 영향이 있을 듯 하여 선뜻 테스트하지 못 하고 있습니다. 해결 방법에 대해서 가이드 부탁드리겠습니다. 감사합니다 !

  • ansible
  • pywinrm
  • python
doore.park 댓글 1 좋아요 0 조회수 639

7.5 시퀄라이즈

해결됨

[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지

안녕하세요 선생님 시퀄라이즈 강의를 듣는데 raw쿼리를 쓰는게 가능하더군요. spring + ibatis 로 주로 개발해와서 raw 쿼리가 익숙한데 실무에서는 시퀄라이즈 vs raw 쿼리 중에 어떤걸 많이 사용하는 편인가요..?

  • node.js
  • mysql
  • mongodb
  • express
  • typescript
  • socket.io
  • jwt
카이 댓글 1 좋아요 1 조회수 274

Premium 문의…!

해결됨

[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지

선생님 어제 코드 감사합니다 아쉽게도 많이 배워야할것같아서 ㅠㅜ 혹시 제가 빠트린 코드가 있을까요 ? 아니면 다른부분 확인할 사항이 있을까요? 작성한 코드 올려드립니다 nodebird-api -> middlewares-> index.js const jwt = require("jsonwebtoken"); //토큰을 검사하는 미들웨어 const rateLimit = require("express-rate-limit"); const User = require("../models/user"); const { Domain } = require("../models/"); const cors = require("cors"); exports.isLoggedIn = (req, res, next) => { if (req.isAuthenticated()) { next(); } else { res.status(403).send("로그인 필요"); } }; exports.isNotLoggedIn = (req, res, next) => { if (!req.isAuthenticated()) { // 패스포트 통해서 로그인 안했으면 next(); } else { const message = encodeURIComponent("로그인한 상태입니다."); res.redirect(`/?error=${message}`); //localhost:8001? error=메시지 } }; //토근검사 exports.verifyToken = (req, res, next) => { try { res.locals.decoded = jwt.verify( req.headers.authorization, process.env.JWT_SECRET ); return next(); } catch (error) { if (error.name === "TokenExpiredError") { return res.status(419).json({ code: 419, message: "토큰이 만료되었습니다.", }); } return res.status(401).json({ code: 401, message: "유효하지 않은 토큰입니다.", }); } }; const limiter = rateLimit({ widowMs: 60 * 1000, max: (req, res) => { if (req.user?.Domains[0]?.type === "premium") { return 10; } return 1; }, handler(req, res) { res.status(this.statusCode).json({ code: this.statusCode, message: `1분에 ${ req.user?.Domains[0]?.type === "premium" ? "열" : "한" } 번만 요청 할 수 있습니다...`, }); }, }); exports.apiLimiter = async (req, res, next) => { let user; if (res.locals.decoded) { user = await User.findOne({ where: { id: res.locals.decoded.id }, include: { model: Domain }, }); } req.user = user; limiter(req, res, next); }; exports.deprecated = (req, res) => { res.status(410).json({ code: 410, message: "새로운 버전이 나왔습니다. 새로운 버전을 사용하세요", }); }; exports.corsWhenDomainMatches = async (req, res, next) => { const domain = await Domain.findOne({ where: { host: new URL(req.get("origin")).host }, }); if (domain) { cors({ origin: true, Credential: true, })(req, res, next); //미들웨어 확장패턴 } else { next(); } }; nodebird-api -> routes -> v2.js const express = require("express"); const { verifyToken, apiLimiter, corsWhenDomainMatches, } = require("../middlewares"); const { createToken, tokenTest, getMyPosts, getPostsByHashtag, } = require("../controllers/v2"); const cors = require("cors"); const router = express.Router(); router.use(corsWhenDomainMatches); router.use( cors({ origin: true, credentials: true, //쿠키요청 }) ); router.post("/token", apiLimiter, createToken); router.get("/test", verifyToken, apiLimiter, tokenTest); router.get("/posts/my", verifyToken, apiLimiter, getMyPosts); // GET /v2/posts/hashtag/:title router.get("/posts/hashtag/:title", verifyToken, apiLimiter, getPostsByHashtag); module.exports = router;

  • node.js
  • mysql
  • mongodb
  • express
  • typescript
  • socket.io
  • jwt
Hongsun1 댓글 1 좋아요 1 조회수 447

10.6 재질문…!

해결됨

[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지

exports.apiLimiter = async (req, res, next) => { let user; if (res.locals.decoded) { user = await User.findOne({ where: { id: res.locals.decoded.id } }); } rateLimit({ widowMs: 60 * 1000, max: user?.type === "premium" ? 10 : 1, handler(req, res) { res.status(this.statusCode).json({ code: this.statusCode, message: "1분에 열 번만 요청 할 수 있습니다...", }); }, })(req, res, next); }; Api 프리미엄고객만 1분에 열번만요청할수있게 미들웨어 확장패턴으로 만들었습니다 그런데 localhost:4000/myposts 접속해서 10 번이상새로고침해도 api가 제한이 안되고 제가작성한 게시글 목록만 뜹니다

  • node.js
  • mysql
  • mongodb
  • express
  • typescript
  • socket.io
  • jwt
Hongsun1 댓글 1 좋아요 0 조회수 602

10.6 사용량 제한 질문

해결됨

[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지

nodebird-api 미들웨어 index.js 타입이 any로 떠서 프리미엄이 체크가 안되고 계속 제가 작성한 게시글정보만 뜹니다 사용량제한 어떻게 하나요? ㅠㅠ const jwt = require("jsonwebtoken"); const rateLimit = require("express-rate-limit"); const User = require("../models/user"); exports.isLoggedIn = (req, res, next) => { if (req.isAuthenticated()) { next(); } else { res.status(403).send("로그인 필요"); } }; exports.isNotLoggedIn = (req, res, next) => { if (!req.isAuthenticated()) { next(); } else { const message = encodeURIComponent("로그인한 상태입니다."); res.redirect(`/?error=${message}`); } }; exports.verifyToken = (req, res, next) => { try { res.locals.decoded = jwt.verify( req.headers.authorization, process.env.JWT_SECRET ); return next(); } catch (error) { if (error.name === "TokenExpiredError") { return res.status(419).json({ code: 419, message: "토큰이 만료되었습니다.", }); } return res.status(401).json({ code: 401, message: "유효하지 않은 토큰입니다.", }); } }; exports.apiLimiter = rateLimit({ windowMs: 60 * 1000, // 1분 max: 1, handler(req, res) { res.status(this.statusCode).json({ code: this.statusCode, // 기본값 429 message: "1분에 한 번만 요청할 수 있습니다.", }); }, }); exports.apiLimiter = async (req, res, next) => { let user; if (res.locals.decoded) { user = await User.findOne({ where: { id: res.locals.decoded.id } }); } rateLimit({ widowMs: 60 * 1000, max: user?.type === "premium" ? 10 : 1, handler(req, res) { res.status(this.statusCode).json({ code: this.statusCode, message: "1분에 열 번만 요청 할 수 있습니다...", }); }, })(req, res, next); }; exports.deprecated = (req, res) => { res.status(410).json({ code: 410, message: "새로운 버전이 나왔습니다. 새로운 버전을 사용하세요", }); };

  • node.js
  • mysql
  • mongodb
  • express
  • typescript
  • socket.io
  • jwt
Hongsun1 댓글 1 좋아요 0 조회수 252

10.6 사용량 제한 구현하기파트 질문

해결됨

[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지

nodebird-api 미들웨어 index.js코드 const jwt = require("jsonwebtoken"); const rateLimit = require("express-rate-limit"); const User = require("../models/user"); exports.isLoggedIn = (req, res, next) => { if (req.isAuthenticated()) { next(); } else { res.status(403).send("로그인 필요"); } }; exports.isNotLoggedIn = (req, res, next) => { if (!req.isAuthenticated()) { next(); } else { const message = encodeURIComponent("로그인한 상태입니다."); res.redirect(`/?error=${message}`); } }; exports.verifyToken = (req, res, next) => { try { res.locals.decoded = jwt.verify( req.headers.authorization, process.env.JWT_SECRET ); return next(); } catch (error) { if (error.name === "TokenExpiredError") { return res.status(419).json({ code: 419, message: "토큰이 만료되었습니다.", }); } return res.status(401).json({ code: 401, message: "유효하지 않은 토큰입니다.", }); } }; exports.apiLimiter = async (req, res, next) => { let user; if (res.locals.decoded) { user = await User.findOne({ where: { id: res.locals.decoded.id } }); } rateLimit({ windowMs: 60 * 1000, max: user?.type === "premium" ? 1000 : 10, handler(req, res) { res.status(this.statusCode).json({ code: this.statusCode, message: "1분에 열 번만 요청할 수 있습니다.", }); }, }); }; exports.deprecated = (req, res) => { res.status(410).json({ code: 410, message: "새로운 버전이 나왔습니다. 새로운 버전을 사용하세요", }); }; nodebird-api routes v2.js 코드 const express = require("express"); const { verifyToken, apiLimiter } = require("../middlewares"); const { createToken, tokenTest, getMyPosts, getPostsByHashtag, } = require("../controllers/v2"); const router = express.Router(); router.post("/token", apiLimiter, createToken); router.get("/test", verifyToken, apiLimiter, tokenTest); router.get("/posts/my", verifyToken, apiLimiter, getMyPosts); router.get("/posts/hashtag/:title", verifyToken, apiLimiter, getPostsByHashtag); module.exports = router; 프리미엄으로 클라이언트 비밀키 발급하고 프리미엄만 사용량제한 구현했는데 터미널에 GET/search/%EA%B3%A0%EC%96%91%EC%9D%B4 - - ms - - 이렇게 나오고 사이트로딩만 뜹니다 ㅜㅜ

  • node.js
  • mysql
  • mongodb
  • express
  • typescript
  • socket.io
  • jwt
댓글 1 좋아요 0 조회수 354

jwt 로그인시 패스워드 검증

미해결

스프링부트 시큐리티 & JWT 강의

JWT 로그인 부분에서 username만 검증하고 password는 검증하는 부분이 안보이는거 같은데 맞나요? @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { System.out.println("UsernamePasswordAuthenticationFilter :: JwtAuthenticationFilter()"); // 1. id, pw 받아서 try { // x-www-form-urlencoded 로 요청시 // BufferedReader br = request.getReader(); // String input = null; // while((input = br.readLine()) != null){ // System.out.println(input); // } // System.out.println(request.getInputStream().toString()); // json 으로 요청시 ObjectMapper om = new ObjectMapper(); User user = om.readValue(request.getInputStream(), User.class); // 토큰 만들기 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()); // PrincipalDetailsService의 loadUserByUsername() 이 실행된 후 정상이면 Authentication이 리턴됨 // DB에 있는 username과 password가 일치한다. Authentication authentication = authenticationManager.authenticate(authenticationToken); // 매니져가 인증을해서 Authentication 객체를 만들어줌 PrincipalDetails principalDetails = (PrincipalDetails) authentication.getPrincipal(); // System.out.println("ㅍㅍㅍ " + principalDetails.getUser().getUsername()); // 이게 조회가 된다는건 로그인 됫다는뜻 // System.out.println("---------------------------------"); // authentication 객체가 Security session 영역에 저장을 해야하고 그방법이 return return authentication; } catch (IOException e) { e.printStackTrace(); // 에러낫을때 떠넘겨 버리면 밑에 코드가 unreacheable 되서 컴파일 에러 } // 2. 정상인지 로그인 시도를 authenticationManager로 하면 PrincipalDetailsService loadUserByUsername() 가 실행됨 // 3. PrincipalDetails 를 세션에 담고 => 세션에 값이 있어야 권한 관리가 된다. (권한관리 안할거면 세션에 안담아도 됨) // 4. JWT 토큰을 만들어서 응답해주면 // System.out.println("================================"); return null; } 사용자 입력(username, password)만 받아서 검증없이 Authentication 객체 만들고 있고 @Service @RequiredArgsConstructor public class PrincipalDetailsService implements UserDetailsService { private final UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { System.out.println("PrincipalDetaiilsService :: loadUserByUsername()"); User userEntity = userRepository.findByUsername(username); System.out.println("DB Connection :: UserRepository"); return new PrincipalDetails(userEntity); } } loadUserByUsername 에서도 username 만 받아서 엔티티 생성하는데 어디서 password 검증도 하는건지 궁금합니다.

  • spring
  • spring-security
  • jwt
Universe New 댓글 1 좋아요 0 조회수 1134

화살표 함수 자동완성이 궁금합니다

해결됨

[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지

안녕하세요 선생님 강의를 보다보니 그림판에서 텍스트로 코딩을 하시는데 화살표함수 작성시 뒤에 구문이 자동으로 입력이 되던데 그림판에 어떤 플러그인을 설치하셨길래 이런 기능이 되는건지 궁금합니다.

  • node.js
  • mysql
  • mongodb
  • express
  • typescript
  • socket.io
  • jwt
카이 댓글 1 좋아요 0 조회수 434

인기 태그

인프런 TOP Writers

주간 인기글