나프2탄 스프1탄
미해결
스프링 프레임워크는 내 손에 [스프1탄]
나프2탄 후반에 스프링에관한 내용이던데 스프1탄을 들을예정이라면 그부분 건너띄고 바로 스프 1들으면 될까요?
- jsp
- spring
- mvc
- spring-security
173만명의 커뮤니티!! 함께 토론해봐요.
미해결
스프링 프레임워크는 내 손에 [스프1탄]
나프2탄 후반에 스프링에관한 내용이던데 스프1탄을 들을예정이라면 그부분 건너띄고 바로 스프 1들으면 될까요?
미해결
스프링 시큐리티
강의 2-2 필터 초기화와 다중 보안 설정을 시큐리티 6.0 버전 이상으로 따라 하던 중에 다른 결과가 나와서 질문 드립니다 configuration은 위와 같이 설정하였고 처음에는 영상에서 처럼 Order(0)과 Order(1)을 지정 하여 실행하였습니다. 제 생각으로는 6.0이상 버전에서는 사용할 수 없는 antMacher대신 requestMatchers를 사용하여 requestMatcher가 1번첫번째 필터체인은 "admin/**" 두번 째 체인은 any request가 될 것이라고 생각했지만 디버깅 해보니 둘 다 any request가 나와 루트로 접속했을 때 첫번째 필터체인이 생성되어 루트로 가는 것이 아닌 basic login form이 나왔습니다. 그래서 디버깅 중 위와 같이 뜨는 것이 보여 애초에 등록이 잘못 된 건가 싶기도 했는데 영상에서처럼 filters를 확인해보면 첫번 째는 httpbasic와 같이 등록된 12개의 필터, 두번 째는 formlogin과 같이 등록된 14개의 필터로 각각 알맞게 등록되어 있었습니다. 어째서 두 개 모두 다 위 필터들은 정상적으로 등록이 되었는데 requestMatcher는 둘 다 any request 인걸까요? 6.0 이후 버전에서는 requestMatcher를 바꾸려면 다른 방법을 써야 하는 것일까요? 도움 주시면 감사하겠습니다 ㅠㅠ
미해결
스프링부트 시큐리티 & JWT 강의
안녕하세요 강사님 객체 전달관련해서 질문이 있습니다. 강의에서 USER 객체를 출력했을 시, 아래와 같이 객체 내부값이 출력되었는데, 저는 `com.want.project.domain.user.domain.Users@6bfd8b8c` 이런식으로 출력이 되네요.. 혹시 tostring을 오버라이딩하신걸까요?? 그리고 제 객체를 getter로 찍어보면 내부에 값이 아닌 null이 저장되어 있는데 이유가 무엇일까요?
미해결
스프링 프레임워크는 내 손에 [스프2탄]
스프2는 스프링부트기반으로 작성하나요? 지금학원에서 스프링부트안적옹해서 시큐리티랑페이징 처리하는걸로프로젝트 하고있거든요
해결됨
스프링 시큐리티
http .authorizeRequests() .antMatchers("/user").hasRole("USER") .antMatchers("/admin").hasRole("ADMIN") .antMatchers("/admin/**").access("hasRole('ADMIN') or hasRole('SYS')") .antMatchers("/admin/**").hasAnyRole("ADMIN","SYS") .anyRequest() .authenticated(); http .formLogin(); http .sessionManagement() .maximumSessions(1) .maxSessionsPreventsLogin(true); http .logout() .logoutUrl("/logout"); 위 내용과 같이 설정 후 user 계정으로 [로그인 -> 로그아웃] 후로그인 시도시 아래와 같은 화면이 표시됩니다. 디버깅 해보니 logout 후에도 기존 세션이 있어 기존 살아있고, 해당 세션과 세션아이디가 다르니 초과 오류를 반환하는 것으로 확인이 됩니다. Logout 후에도 세션이 남아있는 이유와 해결방법이 있을까요?
미해결
스프링부트 시큐리티 & JWT 강의
제가 수업을 놓쳤는지 모르겠는데... 시큐리티가 기본으로 /login을 캐치한다고 들었는데 이 기본값을 바꿀 수 있는 방법이 존재하나요? /login으로 Post 요청을 보내면 attemptAuthentication 메서드를 실행해서 로그인 하는 걸로 알고있는데 /login 값을 제가 원하는 /api/login 처럼 변경할 수 있는지 궁금합니다.
해결됨
스프링 프레임워크는 내 손에 [스프1탄]
안녕하세요. boardInsert 메서드에 @ResponseBody를 붙이는 이유가 궁금합니다. insert 후에 리턴 값이 없는데 왜 @ResponseBody를 붙이는 건가요? callback 함수인 loadList 때문인가요? 감사합니다.
미해결
스프링부트 시큐리티 & JWT 강의
public class LoginFilter extends UsernamePasswordAuthenticationFilter 이 작업을 통해서 login으로 들어오는 것을 @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try { User user = objectMapper.readValue(request.getInputStream(), User.class); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()); return authenticationManager.authenticate(authenticationToken); } catch (IOException e) { log.error("{}", e); } return super.attemptAuthentication(request, response); } 이 작업을 통해서 로그인 하는 걸로 알고있는데, 이러한 경우에 컨트롤러 테스트 코드를 어떻게 작성할 수 있는 건지 궁금합니다. api가 아니라 자꾸 에러가 발생하는데 혹시 이러한 경우에 api 테스트 코드 작성 팁좀 가르쳐주실 수 있나요? 이러한 예외가 발생합니다. 이유를 모르겠습니다. Postman으로 할 때는 잘 동작합니다 ㅜㅜ 그냥 /api/login 이라는 api를 생성해서 테스트해야 될까요?
미해결
스프링 시큐리티
강의 소스 그대로 참고하여 현재 진행중인 프로젝트에 CustomUserDetailsService, CustomAuthenticaionProvider, SecurityConfigure 모두 작성하였습니다. 그런데 CustomAuthenticaionProvider의 UserDetailsService와 PasswordEncoder의 @Autowired 부분에서 빨간 줄이 그어져 진행이 불가능합니다. 혹시 스프링 부트 버전차이 때문에 그러는 걸까요?? 현재 2.7.1 사용중입니다. 실습한 코드 사진 첨부하도록 하겠습니다. configure context customUserDetailsService customAuthenticationProvider
미해결
스프링 시큐리티
안녕하세요 강의 디버깅중에 저희가 본 내용은 여기입니다. 그런데 위에 보시면 169라인 exception instanceof AuthenticationException 인증예외도 잡더라구요. 그런데 저부분은 현재 강의에선 이용하지않고 지금 강의는 AuthenticationFailureHandler 를 별도로 상속받아 클래스를 만들어 처리하고 있다고 생각하면 될까요 ?
미해결
스프링 시큐리티
안녕하세요 강의 너무 최고입니다. 질문이 있습니다. !! 첫번째 질문은 http .exceptionHandling() .authenticationEntryPoint( new AjaxLoginAuthenticationEntryPoint() ) .accessDeniedHandler( ajaxAccessDeniedHandler() ); 설정하실때 전자는 생성자를 만들고 두번째는 왜 같은 방법으로 생성자를 만들지않고 등록을 하는건가요 ? -> .accessDeniedHandler( ajaxAccessDeniedHandler() ); 이걸 .accessDeniedHandler( new AjaxAccessDeniedHandler() ); 이렇게 안하시는 이유가 있나요 ? 두번째 질문은 SecurityConfig 에서 @Bean public AccessDeniedHandler accessDeniedHandler() { CustomAccessDeniedHandler accessDeniedHandler = new CustomAccessDeniedHandler(); accessDeniedHandler.setErrorPage("/denied"); return accessDeniedHandler; } 이 등록되어있는데 .exceptionHandling() .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")) .accessDeniedPage("/denied") .accessDeniedHandler(accessDeniedHandler()); 이 두줄을 추가 하셨습니다. 우선적으로 여기서 첫번째 질문은 .accessDeniedPage("/denied")랑 accessDeniedHandler.setErrorPage("/denied"); 이게 같은 역할을 하는것이 아닌가요 ? 그리고 마지막으로 .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")) 이 내용은 CustomAuthenticationFailureHandler 에 설정된 setDefaultFailureUrl("/login?error=true&exception=" +errorMessage); super.onAuthenticationFailure(request,response, exception); 이게 이미 처리하고 있던 내용 아닌가요 ? 마지막 질문은 .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")) 이건 인가 예외로 생각하고 CustomAuthenticationFailureHandler 에 설정된 내용은 인증 내용이라고 생각하면 될까요 ?
미해결
호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
안녕하세요 호돌맨 선생님! 세션토큰 발급기능 추가를 초반에 보다가 로그인 관련 테스트를 직접 작성해보라고 하셔서 해봤는데 궁금한 점이 있어서 질문드립니다! 작성한코드 @Test @DisplayName("인증 테스트") void test11() throws Exception { // given Member member = Member.builder() .email("abc@naver.com") .password("1234") .build(); // UserRepository를 MemeberRepository로 이름 변경 했습니다. memberRepository.save(member); Login login = Login.builder() .email("abc@naver.com") .password("1234") .build(); String json = objectMapper.writeValueAsString(login); // expected mockMvc.perform(post("/auth/login") .contentType(APPLICATION_JSON) .content(json) ) .andExpect(status().isOk()) .andDo(print()); 결과 query did not return a unique result: 2; nested exception is javax.persistence.NonUniqueResultException: query did not return a unique result: 2 원인 JPA 에러라는 것을 검색을 통해서 알았습니다 해결 방법 블로그 주소 : https://wakestand.tistory.com/943 UserRepository에 작성한 findByEmailAndPassWord 메서드가 Optional로 되어 있는데 이 부분을 List로 변경해야할 것 같아서 다음과 같이 변경했습니다 변경된 코드 UserRepository public interface MemberRepository extends CrudRepository<Member, Long> { List<Member> findByEmailAndPassword(String email, String password); } AuthService @Service @RequiredArgsConstructor public class AuthService { private final MemberRepository memberRepository; public List<Member> signin(Login login) { List<Member> memberList = Optional.ofNullable(memberRepository.findByEmailAndPassword(login.getEmail(), login.getPassword())) .orElseThrow(() -> new InvalidSign()); return memberList; } } 위 처럼 Optional<Member가 아닌 List<Member>로 변경해서 테스트 로직을 성공하긴했습니다... 질문 UserRepository의 Optional<Member>를 변경하지 않고 테스트 코드를 작성하는 방법이 있을까요?? Optional에서 List로 변경했을 때 문제점은 없을까요?
미해결
스프링 프레임워크는 내 손에 [스프2탄]
8강 스프2탄_Junit리스트보기 Controller테스트 강의 중 질문입니다. 테스트코드 BoardControllerTest.java에서 @Test public void testList() throws Exception{ log.info ( mockMvc.perform(MockMvcRequestBuilders. get ("/board/list")) .andReturn() .getModelAndView().getModelMap()); } 굵은 글씨 get이라고 하셨는데 BoardController.java에서 @GetMapping ("/list") public String getList(Model model) { List<Board> list=boardService.getList(); // 객체바인딩 model.addAttribute("list",list); return "board/boardList"; } 굵은 글씨 @GetMapping 이라고 해줘서 테스트코드에서 get인 것은 알겠는데 왜 get이라고 해 줬는데 이해가 잘 안되서요.. 특별한 거 없으면 그냥 get이라고 해주면 되는 건가요? 두번째 질문은 다음 강의 언제 나오나요
미해결
스프링 프레임워크는 내 손에 [스프1탄]
현재 스프1탄 mvc01버전 듣고 있는데 hikari관련 오류가 나서 깃허브가서 소스코드를 가져오려고 합니다. 깃 들어가니 레포지토리가 정리가 잘 안되어있어서 찾지를 못하겠는데 어떤 레포지토리를 봐야하나요???
해결됨
스프링 시큐리티
protected AjaxLoginProcessingFilter(String defaultFilterProcessesUrl) { super(new AntPathRequestMatcher("/xxx")); } 이렇게 설정하는건 자바에서 직접 설정하는거고 이전에 설정파일에서 http .authorizeRequests().antMatchers("/xxx") 경로 설정한거랑 두개다 같다고 생각하면 되는거죠 ?
미해결
스프링 시큐리티
인증 거부 처리 -8:42 ) LoginController 에서 Account account = (Account) authentication.getPrincipal(); 이 가능 한 이유가 저희가 인증 객체를 CustomAuthenticationProvider코드에서 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( accountContext.getAccount() , null, accountContext.getAuthorities()); 로 토큰을 만들어서 스프링이 내부적으로 SecurityContext 안에 accountContext.getAccount(); (인증객체) 을 저장했기 때문에 가능하다고 이해했는데 맞나요 ?
미해결
스프링 시큐리티
안녕하세요 기존에는 CustomUserDetailsService 로 인증처리를 완료했는데 이때 AuthenticationProvider의 anthenticate 메서드 역할은 우리가 만든 loadUserByUsername + 스프링 시큐리티가 기본으로 제공하는 역할을 이용하였고 이번에 새로 추가한 CustomAuthenticationProvider 는 AuthenticationProvider의 anthenticate 역할을 커스터마이징 했다고 생각하면 되는걸까요 ?
미해결
스프링부트 시큐리티 & JWT 강의
https://github.com/bgseong/Security-test public class JwtAuthorizationFilter extends BasicAuthenticationFilter { private TokenService tokenService; public JwtAuthorizationFilter(AuthenticationManager authenticationManager, TokenService tokenService) { super(authenticationManager); this.tokenService = tokenService; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { super.doFilterInternal(request, response, chain); String token = tokenService.resolveToken(request); if(token == null){ chain.doFilter(request, response); return; } if (tokenService.validateToken(token)) { Authentication authentication = tokenService.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); System.out.println(SecurityContextHolder.getContext().getAuthentication()); } chain.doFilter(request,response); } } @EnableWebSecurity @Configuration @RequiredArgsConstructor @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig { @Autowired TokenService tokenService; @Autowired CorsConfig corsConfig; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{ http .csrf().disable() .httpBasic().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .formLogin().disable() .apply(new MyCustomDsl()) .and() .authorizeHttpRequests(authorize -> authorize .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() // 특정 정적 리소스 허용 .requestMatchers("/api/v1/user/**").hasAnyRole("ADMIN", "MANAGER") .requestMatchers("/api/v1/manager/**").hasRole("ADMIN") .requestMatchers("/api/v1/admin/**").hasRole("ROLE_ADMIN") .anyRequest().permitAll()); return http.build(); } public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> { @Override public void configure(HttpSecurity http) throws Exception { AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class); http .addFilter(corsConfig.corsFilter()) .addFilter(new LoginFilter(authenticationManager,tokenService)) .addFilter(new JwtAuthorizationFilter(authenticationManager,tokenService)); } } } @Component public class TokenService implements InitializingBean { private final UserRepository usersrepository; private final Logger logger = LoggerFactory.getLogger(TokenService.class); private static final String AUTHORITIES_KEY = "auth"; private final String secret; private final long accessTokenValidityInMilliseconds; private final long refreshTokenValidityInMilliseconds; public static final String AUTHORIZATION_HEADER = "Authorization"; public static final String REFRESHTOKEN_HEADER = "RefreshToken"; private Key key; public TokenService( UserRepository usersrepository, @Value("${spring.jwt.secret}") String secret, @Value("${spring.jwt.token-validity-in-seconds}") long tokenValidityInSeconds) { this.usersrepository = usersrepository; this.secret = secret; this.accessTokenValidityInMilliseconds = tokenValidityInSeconds * 500; this.refreshTokenValidityInMilliseconds = tokenValidityInSeconds * 1000 * 336; } @Override public void afterPropertiesSet() { byte[] keyBytes = Decoders.BASE64.decode(secret); this.key = Keys.hmacShaKeyFor(keyBytes); } public String createAccessToken(PrincipalDetails principalDetails) { return createAccessToken(principalDetails.getUser().getEmail(), principalDetails.getAuthorities()); } public String createRefreshToken(PrincipalDetails principalDetails) { return createRefreshToken(principalDetails.getUser().getEmail(), principalDetails.getAuthorities()); } public String createAccessToken(String email, Collection<? extends GrantedAuthority> inputAuthorities) { String authorities = inputAuthorities.stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.joining(",")); long now = (new Date()).getTime(); String accessToken = Jwts.builder() .setSubject(email) .claim(AUTHORITIES_KEY, authorities) .signWith(key, SignatureAlgorithm.HS512) .setExpiration(new Date(now + this.accessTokenValidityInMilliseconds)) .compact(); return accessToken; } public String createRefreshToken(String email, Collection<? extends GrantedAuthority> inputAuthorities) { String authorities = inputAuthorities.stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.joining(",")); long now = (new Date()).getTime(); String Token = Jwts.builder() .setSubject(email) .claim(AUTHORITIES_KEY, authorities) .signWith(key, SignatureAlgorithm.HS512) .setExpiration(new Date(now + this.refreshTokenValidityInMilliseconds)) .compact(); return Token; } public Authentication getAuthentication(String token) { Claims claims = Jwts .parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(token) .getBody(); User user = usersrepository.findByEmail(claims.get("sub",String.class)); Collection<? extends GrantedAuthority> authorities = Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); PrincipalDetails principal = new PrincipalDetails(user); return new UsernamePasswordAuthenticationToken(principal, null, authorities); } public String resolveToken(HttpServletRequest request) { String bearerToken = request.getHeader(AUTHORIZATION_HEADER); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return null; } public boolean validateToken(String token) { try { Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token); return true; } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) { logger.info("worng JWT sign"); } catch (ExpiredJwtException e) { logger.info("expire JWT"); } catch (UnsupportedJwtException e) { logger.info("No support JWT"); } catch (IllegalArgumentException e) { logger.info("JWT is worng"); } return false; } } 이렇게 구성해 놨습니다. 그런데 모든 권한이 적용된 url에 접근을 하면 403 에러가 뜹니다. 필터에서 SecurityContextHolder를 출력하면 아래와 같이 출력이 되는 걸 확인했고 [Principal=com.securitytest.Securitytest.auth.PrincipalDetails@45095607, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_ADMIN]] 컨트롤러에서 PrincipalDetails를 호출해보니, null이라서 오류가 난다고 뜹니다. 무엇이 문제일까요ㅠㅠ..
미해결
스프링부트 시큐리티 & JWT 강의
설정 후 run 하면 If you want an embedded database (H2, HSQL or Derby), please put it on the classpath. 라는 문구가 뜹니다. 그래서 mysql에 다시 한번 sql문구를 쓰고 다시 처음부터 강의대로 해봤는데도 안되서 구글링해도 모르겠어서요ㅠㅠ
해결됨
호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)
https://github.com/incheol789/hodolog.git 깃허브에 파일 올려두었습니다.. 날이 많이 덥습니다.. 더위 조심하세요!