728x90


일단 차이점을 찾아보니

  • Stream.toList() return -> 변경할수 없는 List 이고 추가,삭제 불가
  • Collectors.toList() return -> 일반적인 List 이고 추가,삭제 가능
  • Collectors.toUnmodifiableList() return -> 변경할수 없는 List 이고 추가,삭제 불가
  • 성능관련해서 테스트 해보았다.
@BenchmarkMode(Mode.All)
@Fork(1)
@State(Scope.Thread)
@Warmup(iterations = 20, time = 1, batchSize = 10000)
@Measurement(iterations = 20, time = 1, batchSize = 10000)
public class BenchmarkStreamToList {

    @Benchmark
    public List<Integer> streamToList() {
        return IntStream.range(1, 1000).boxed().toList();
    }

    @Benchmark
    public List<Integer> collectorsToList() {
        return IntStream.range(1, 1000).boxed().collect(Collectors.toList());
    }

    @Benchmark
    public List<Integer> streamToList() {
        return IntStream.range(1, 1000).boxed().toList();
    }

}

결과:

Benchmark                                                                                  Mode  Cnt   Score    Error  Units
BenchmarkStreamToList.collectorsToList                                                    thrpt   20  24.422 ±  0.268  ops/s
BenchmarkStreamToList.collectorsToUnmodifiableList                                        thrpt   20  22.784 ±  0.599  ops/s
BenchmarkStreamToList.streamToList                                                        thrpt   20  31.779 ±  1.732  ops/s
BenchmarkStreamToList.collectorsToList                                                     avgt   20   0.045 ±  0.006   s/op
BenchmarkStreamToList.collectorsToUnmodifiableList                                         avgt   20   0.062 ±  0.035   s/op
BenchmarkStreamToList.streamToList                                                         avgt   20   0.040 ±  0.028   s/op
BenchmarkStreamToList.collectorsToList                                                   sample  445   0.046 ±  0.002   s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.00                            sample        0.039            s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.50                            sample        0.041            s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.90                            sample        0.057            s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.95                            sample        0.073            s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.99                            sample        0.102            s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.999                           sample        0.150            s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.9999                          sample        0.150            s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p1.00                            sample        0.150            s/op
BenchmarkStreamToList.collectorsToUnmodifiableList                                       sample  460   0.044 ±  0.001   s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.00    sample        0.042            s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.50    sample        0.044            s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.90    sample        0.046            s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.95    sample        0.047            s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.99    sample        0.051            s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.999   sample        0.057            s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.9999  sample        0.057            s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p1.00    sample        0.057            s/op
BenchmarkStreamToList.streamToList                                                       sample  655   0.031 ±  0.001   s/op
BenchmarkStreamToList.streamToList:streamToList·p0.00                                    sample        0.030            s/op
BenchmarkStreamToList.streamToList:streamToList·p0.50                                    sample        0.031            s/op
BenchmarkStreamToList.streamToList:streamToList·p0.90                                    sample        0.032            s/op
BenchmarkStreamToList.streamToList:streamToList·p0.95                                    sample        0.033            s/op
BenchmarkStreamToList.streamToList:streamToList·p0.99                                    sample        0.035            s/op
BenchmarkStreamToList.streamToList:streamToList·p0.999                                   sample        0.037            s/op
BenchmarkStreamToList.streamToList:streamToList·p0.9999                                  sample        0.037            s/op
BenchmarkStreamToList.streamToList:streamToList·p1.00                                    sample        0.037            s/op
BenchmarkStreamToList.collectorsToList                                                       ss   20   0.043 ±  0.001   s/op
BenchmarkStreamToList.collectorsToUnmodifiableList                                           ss   20   0.045 ±  0.004   s/op
BenchmarkStreamToList.streamToList                                                           ss   20   0.031 ±  0.001   s/op

결론: Stream.toList()
성능은 모든 방면에서
Collectors.toList() Collectors.toUnmodifiableList()를 앞지른다!

  • 다음은 조금 더 큰 데이터를 세팅해보자!
@Benchmark
public List<Integer> streamToList() {
  return IntStream.range(1, 10000).boxed().toList();
}

@Benchmark
public List<Integer> collectorsToList() {
  return IntStream.range(1, 10000).boxed().collect(Collectors.toList());
}

@Benchmark
public List<Integer> streamToList() {
  return IntStream.range(1, 10000).boxed().toList();
}

결과:

Benchmark                                                                                  Mode  Cnt  Score   Error  Units
BenchmarkStreamToList.collectorsToList                                                    thrpt   20  2.186 ± 0.162  ops/s
BenchmarkStreamToList.collectorsToUnmodifiableList                                        thrpt   20  2.184 ± 0.042  ops/s
BenchmarkStreamToList.streamToList                                                        thrpt   20  3.538 ± 0.058  ops/s
BenchmarkStreamToList.collectorsToList                                                     avgt   20  0.426 ± 0.004   s/op
BenchmarkStreamToList.collectorsToUnmodifiableList                                         avgt   20  0.469 ± 0.016   s/op
BenchmarkStreamToList.streamToList                                                         avgt   20  0.293 ± 0.008   s/op
BenchmarkStreamToList.collectorsToList                                                   sample   58  0.448 ± 0.049   s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.00                            sample       0.414           s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.50                            sample       0.422           s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.90                            sample       0.458           s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.95                            sample       0.560           s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.99                            sample       1.160           s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.999                           sample       1.160           s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p0.9999                          sample       1.160           s/op
BenchmarkStreamToList.collectorsToList:collectorsToList·p1.00                            sample       1.160           s/op
BenchmarkStreamToList.collectorsToUnmodifiableList                                       sample   60  0.458 ± 0.004   s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.00    sample       0.447           s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.50    sample       0.455           s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.90    sample       0.471           s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.95    sample       0.482           s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.99    sample       0.492           s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.999   sample       0.492           s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p0.9999  sample       0.492           s/op
BenchmarkStreamToList.collectorsToUnmodifiableList:collectorsToUnmodifiableList·p1.00    sample       0.492           s/op
BenchmarkStreamToList.streamToList                                                       sample   78  0.293 ± 0.012   s/op
BenchmarkStreamToList.streamToList:streamToList·p0.00                                    sample       0.277           s/op
BenchmarkStreamToList.streamToList:streamToList·p0.50                                    sample       0.284           s/op
BenchmarkStreamToList.streamToList:streamToList·p0.90                                    sample       0.309           s/op
BenchmarkStreamToList.streamToList:streamToList·p0.95                                    sample       0.377           s/op
BenchmarkStreamToList.streamToList:streamToList·p0.99                                    sample       0.459           s/op
BenchmarkStreamToList.streamToList:streamToList·p0.999                                   sample       0.459           s/op
BenchmarkStreamToList.streamToList:streamToList·p0.9999                                  sample       0.459           s/op
BenchmarkStreamToList.streamToList:streamToList·p1.00                                    sample       0.459           s/op
BenchmarkStreamToList.collectorsToList                                                       ss   20  0.474 ± 0.133   s/op
BenchmarkStreamToList.collectorsToUnmodifiableList                                           ss   20  0.493 ± 0.099   s/op
BenchmarkStreamToList.streamToList                                                           ss   20  0.325 ± 0.056   s/op

결론: 데이터를 크게 셋팅해도 Stream.toList() 성능은 모든 방면에서 Collectors.toList() Collectors.toUnmodifiableList()를 앞지른다!

괜히 테스트 했나 ????

끝!

728x90

SpringBoot version:2.1.6

SpringSecurity version: 5.1.5

MyBatis-Plus version: 3.1.0

JDK version:1.8 (17에서도 잘 돌아감)

 

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

application.yml

# 포트
server:
  port: 8080
spring:
  # datasource 설정
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo_security?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
# jwt 설정
jwt:
  secret: JWTSecret
  tokenHeader: Authorization
  tokenPrefix: Sans-

  expiration: 86400 # 1day ,604800 1 weekly
  # 인증필요없는 url
  antMatchers: /index/**,/login/**,/favicon.ico
# Mybatis-plus 관련 설정
mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  global-config:
    db-config:
      id-type: AUTO
      field-strategy: NOT_EMPTY
      db-type: MYSQL
  configuration:
    map-underscore-to-camel-case: true
    call-setters-on-nulls: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

기본적인 Entity,Dao,Service 와 SpringSecurity user Entity,Service 부분은 여기서 생략한다.

Jwt에 사용된 util 작성

@Slf4j
public class JWTTokenUtil {

    private JWTTokenUtil(){}

    public static String createAccessToken(SelfUserEntity selfUserEntity){
        // jwt 생성 부분
        String token = Jwts.builder()
                .setId(selfUserEntity.getUserId()+"")
                .setSubject(selfUserEntity.getUsername())
                // 발급시간
                .setIssuedAt(new Date())
                // 발급자 임의로 수정가능
                .setIssuer("demo-user")
                // 권한설정
                .claim("authorities", JSON.toJSONString(selfUserEntity.getAuthorities()))
                // 만료시간
                .setExpiration(new Date(System.currentTimeMillis() + JWTConfig.expiration))
                // 싸인알고리즘, yml에 기재된 시큐릿 key값
                .signWith(SignatureAlgorithm.HS512, JWTConfig.secret)
                .compact();
        return token;
    }
}

권한없을시 처리될 부분을 작성한다.

@Component
public class UserAuthAccessDeniedHandler implements AccessDeniedHandler{

    /**
     * 권한없을시 반환하는 부분
     * @param request
     * @param response
     * @param exception
     */
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception){
        ResultUtil.responseJson(response,ResultUtil.resultCode(403,"권한없음!"));
    }
}

사용자 미등록시 처리되는 부분

@Component
public class UserAuthenticationEntryPointHandler implements AuthenticationEntryPoint {

    /**
     * 로그인하지 않는 사용자일때 반환하는 부분
     * @param request
     * @param response
     * @param exception
     */
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){
        ResultUtil.responseJson(response,ResultUtil.resultCode(401,"로그인되지않음"));
    }
}

로그인실패시 처리될 부분작성

@Slf4j
@Component
public class UserLoginFailureHandler implements AuthenticationFailureHandler {

    /**
     * 로그인 실패시 리턴
     * @param request
     * @param response
     * @param exception
     */
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){

        if (exception instanceof UsernameNotFoundException){
            log.info("[로그인실패!]"+exception.getMessage());
            ResultUtil.responseJson(response,ResultUtil.resultCode(500,"존재하지 않는 사용자!"));
        }
        if (exception instanceof LockedException){
            log.info("[로그인실패!]"+exception.getMessage());
            ResultUtil.responseJson(response,ResultUtil.resultCode(500,"동결된 사용자!"));
        }
        if (exception instanceof BadCredentialsException){
            log.info("[로그인실패!]"+exception.getMessage());
            ResultUtil.responseJson(response,ResultUtil.resultCode(500,"패스워드가 틀림."));
        }
        ResultUtil.responseJson(response,ResultUtil.resultCode(500,"로그인 실패!"));
    }
}

로그인 성공시 처리될 부분작성

@Slf4j
@Component
public class UserLoginSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
        SelfUserEntity selfUserEntity =  (SelfUserEntity) authentication.getPrincipal();
        String token = JWTTokenUtil.createAccessToken(selfUserEntity);
        token = JWTConfig.tokenPrefix + token;

        Map<String,Object> resultData = new HashMap<>();
        resultData.put("code","200");
        resultData.put("msg", "로그인성공!");
        resultData.put("token",token);
        ResultUtil.responseJson(response,resultData);
    }
}

로그아웃처리 부분

@Component
public class UserLogoutSuccessHandler implements LogoutSuccessHandler {

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
        Map<String,Object> resultData = new HashMap<>();
        resultData.put("code","200");
        resultData.put("msg", "로그인성공!");
        SecurityContextHolder.clearContext();
        ResultUtil.responseJson(response,ResultUtil.resultSuccess(resultData));
    }
}

로그인 인증서 핵심로직부분이다.

@Component
public class UserAuthenticationProvider implements AuthenticationProvider {
    @Autowired
    private SelfUserDetailsService selfUserDetailsService;
    @Autowired
    private SysUserService sysUserService;
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String userName = (String) authentication.getPrincipal();
        String password = (String) authentication.getCredentials();
        SelfUserEntity userInfo = selfUserDetailsService.loadUserByUsername(userName);
        if (userInfo == null) {
            throw new UsernameNotFoundException("사용자존재하지않음");
        }
        if (!new BCryptPasswordEncoder().matches(password, userInfo.getPassword())) {
            throw new BadCredentialsException("패스워드틀림");
        }
        if (userInfo.getStatus().equals("PROHIBIT")){
            throw new LockedException("사용자가동결됨 ");
        }
        Set<GrantedAuthority> authorities = new HashSet<>();
        List<SysRoleEntity> sysRoleEntityList = sysUserService.selectSysRoleByUserId(userInfo.getUserId());
        for (SysRoleEntity sysRoleEntity: sysRoleEntityList){
            authorities.add(new SimpleGrantedAuthority("ROLE_" + sysRoleEntity.getRoleName()));
        }
        userInfo.setAuthorities(authorities);
        return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
    }
    @Override
    public boolean supports(Class<?> authentication) {
        return true;
    }
}

PermissionEvaluator 컨트롤러에 어노테이션으로 체크하는 인증부분 구현 하는 부분이다.

@Component
public class UserPermissionEvaluator implements PermissionEvaluator {
    @Autowired
    private SysUserService sysUserService;


    @Override
    public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) {
        SelfUserEntity selfUserEntity =(SelfUserEntity) authentication.getPrincipal();
        Set<String> permissions = new HashSet<>();
        List<SysMenuEntity> sysMenuEntityList = sysUserService.selectSysMenuByUserId(selfUserEntity.getUserId());
        for (SysMenuEntity sysMenuEntity:sysMenuEntityList) {
            permissions.add(sysMenuEntity.getPermission());
        }
        if (permissions.contains(permission.toString())){
            return true;
        }
        return false;
    }
    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        return false;
    }
}

SpringSecurity 핵심 비지니스로직 구현 부분이다.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 요거 추가하면 controller 쪽에 어노테이션으로 권한 처리 할수 있음.
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserLoginSuccessHandler userLoginSuccessHandler;

    @Autowired
    private UserLoginFailureHandler userLoginFailureHandler;

    @Autowired
    private UserLogoutSuccessHandler userLogoutSuccessHandler;

    @Autowired
    private UserAuthAccessDeniedHandler userAuthAccessDeniedHandler;

    @Autowired
    private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;

    @Autowired
    private UserAuthenticationProvider userAuthenticationProvider;


    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler(){
        DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
        handler.setPermissionEvaluator(new UserPermissionEvaluator());
        return handler;
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth){
        auth.authenticationProvider(userAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
               .antMatchers(JWTConfig.antMatchers.split(",")).permitAll()
                .anyRequest().authenticated()
                .and()

                .httpBasic().authenticationEntryPoint(userAuthenticationEntryPointHandler)
                .and()
                .formLogin()
                .loginProcessingUrl("/login/userLogin")
                .successHandler(userLoginSuccessHandler)
                .failureHandler(userLoginFailureHandler)
                .and()

                .logout()
                .logoutUrl("/login/userLogout")
                .logoutSuccessHandler(userLogoutSuccessHandler)
                .and()
                .exceptionHandling().accessDeniedHandler(userAuthAccessDeniedHandler)
                .and()
                .cors()
                .and()
                .csrf().disable();

        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.headers().cacheControl();
        http.addFilter(new JWTAuthenticationTokenFilter(authenticationManager()));
    }
}

JWT 필터도 만들어봐야지

@Slf4j
public class JWTAuthenticationTokenFilter extends BasicAuthenticationFilter {

    public JWTAuthenticationTokenFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // token 헤더정보 가져오기
        String tokenHeader = request.getHeader(JWTConfig.tokenHeader);
        if (null!=tokenHeader && tokenHeader.startsWith(JWTConfig.tokenPrefix)) {
            try {
                String token = tokenHeader.replace(JWTConfig.tokenPrefix, "");
                // jwt 파싱
                Claims claims = Jwts.parser()
                        .setSigningKey(JWTConfig.secret)
                        .parseClaimsJws(token)
                        .getBody();
                // user정보
                String username = claims.getSubject();
                String userId=claims.getId();
                if(!StringUtils.isEmpty(username)&&!StringUtils.isEmpty(userId)) {
                    // role 정보
                    List<GrantedAuthority> authorities = new ArrayList<>();
                    String authority = claims.get("authorities").toString();
                    if(!StringUtils.isEmpty(authority)){
                        List<Map<String,String>> authorityMap = JSONObject.parseObject(authority, List.class);
                        for(Map<String,String> role : authorityMap){
                            if(!StringUtils.isEmpty(role)) {
                                authorities.add(new SimpleGrantedAuthority(role.get("authority")));
                            }
                        }
                    }
                    // 파라미터 셋팅
                    SelfUserEntity selfUserEntity = new SelfUserEntity();
                    selfUserEntity.setUsername(claims.getSubject());
                    selfUserEntity.setUserId(Long.parseLong(claims.getId()));
                    selfUserEntity.setAuthorities(authorities);
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(selfUserEntity, userId, authorities);
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            } catch (ExpiredJwtException e){
                log.info("Token 유효기간 만료");
            } catch (Exception e) {
                log.info("Token 유효하지 않는 토큰");
            }
        }
        filterChain.doFilter(request, response);
    }
}

테스트 코드도 작성 , (회원가입이 잘되는지... 물론 회원가입 controller는 따론 구현하지 않아서 일단 이런 식으로 강제로 user를 생성해 본다.)

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootSecurityDemoApplicationTests {


    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    @Autowired
    private SysUserRoleService sysUserRoleService;



    @Test
    public void contextLoads() {
        // 가입
        SysUserEntity sysUserEntity = new SysUserEntity();
        sysUserEntity.setUsername("user");
        sysUserEntity.setPassword(bCryptPasswordEncoder.encode("123456"));
        // 사용자 status
        sysUserEntity.setStatus("NORMAL");
        sysUserService.save(sysUserEntity);
        // role setting 1:ADMIN 2:USER
        SysUserRoleEntity sysUserRoleEntity = new SysUserRoleEntity();
        sysUserRoleEntity.setRoleId(2L);
        sysUserRoleEntity.setUserId(sysUserEntity.getUserId());
        sysUserRoleService.save(sysUserRoleEntity);
    }

}

postman 으로 조금전에 생성한 사용자로 로그인 해보자

짜쟌~~

user 정보 조회도 해보자

admin 으로 접근을 하니 권한이 없다고 나온다. 물론 admin 권한은 모두 볼수 있어야 되는거 아니야 ? 라고 할수 있는데 해당 demo에는 그런거 따로 설정을 안해서 이다.

테스트 코드로 관리자 admin 이라는 사용자 생성 , role : ADMIN 으로 설정하고 다시 로그인 하여 새로은 admin jwt 을 받고 

/admin/info api를 호출하면 권한이 정상으로 인증되서 정보를 정상조회할수 있다.

일단 잘되니깐 나머지 controller도 만들어서 테스트하면 될것다.

 

인터넷에 찾아보면 너무 많은 방대한 가이드들이 있긴 하지만 한번 이대로 따라 해보는것도 좋을것다.

그리고 Spring Security 5.7.0 이전/이후 버전으로 설정이 조금 다르다.

해당글은 Spring Security 5.7.0 이전 버전으로 진행한것이다.

추후 업그레이드된 Spring Security 5.7.0 이후 버전 설정 방법으로 글을 쓰도록 하겠다.

 

아참 혹시나 필요할것 같아서 소스 코드도 깃에 업로드 합니다.

필요한 분은 클론 받으시고 비록 정답은 아니지만 프로젝트 적용에 조금이나 도움되엇으면 합니다.

https://github.com/luxury515/spring-boot-security-demo

끝!

 

참고문서

https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter

https://github.com/spring-projects/spring-security/issues/10822

728x90

vue 개발지 도움될만한 놈들이니 빨리 저장하시오~!

1. vueuse

GitHub 에서 12.8k star 를 받을 정도로 인기가 있다.

import { useLocalStorage, useMouse, usePreferredDark } from "@vueuse/core";

export default {
    setup() {
        // tracks mouse position
        const { x, y } = useMouse();

        // is user prefers dark theme
        const isDark = usePreferredDark();

        // persist state in localStorage
        const store = useLocalStorage("my-storage", {
            name: "Apple",
            color: "red",
        });

        return { x, y, isDark, store };
    },
};

GitHub:https://github.com/vueuse/vueuse

2. vue-js-modal

인기는 요정도 ? Github 4.2k star.

모달이 조금 더 쎄련된 모습으로 개발할수 있다.

GitHub:https://github.com/euvl/vue-js-modal

3. vue-wait

로딩바로 유용하게 사용될 라이브러리  Github 에서 받은 star는  1.9k 개정도.

GitHub:github.com/f/vue-wait

4. good-table

소팅,필터,페이징,구룹핑 등 다양한 기능를 구현할수 있어서 자주사용된다.GitHub  의 인기는 2k star 정도

GitHub:https://github.com/xaksis/vue-good-table

5. vue-notification

성공,실패,경고시 내보내는 노티들을 유용하게 다둘수 있는 놈. 전통적인 녹,황,빨 3가지 색을 사용하고 많은 기능들을 제공한다.

애니메이션 , 노출 위치 , 커스텀 양식 등등. Github에서 약 2.3k star 를 받았다.

GitHub:https://github.com/euvl/vue-notification

6. tree select

단일선택,멀티선택,자동완성,비동기검색, 지연로딩 등등 .GitHub 인기는 약 2.6K star임.

가끔 개발자 사이트에 자신의 기술스택 선택할때 봣었는데 ...

GitHub:https://github.com/riophae/vue-treeselect

7. egjs-infinite grid

그리드 사용시 강추!

import { MasonryInfiniteGrid } from "@egjs/infinitegrid";

function getItems(nextGroupKey, count) {
    const nextItems = [];

    for (let i = 0; i < count; ++i) {
        const num = nextGroupKey * count + i;
        nextItems.push(`<div class="item"></div>`);
    }
    return nextItems;
}
const ig = new MasonryInfiniteGrid(".container", {
    gap: 5,
});

ig.on("requestAppend", (e) => {
    const nextGroupKey = (+e.groupKey || 0) + 1;

    ig.append(getItems(nextGroupKey, 10), nextGroupKey);
});
ig.renderItems();

GitHub:https://github.com/naver/egjs-infinitegrid


끝!
728x90

2022년 12월, GPT3.5 기반 ChatGPT의 베타버전이 공개되었다.

오늘은 chatGPT 라는 놈을 체험해보았다.

챗봇과 대화형식으로 원하는 코드를 말하면 그에 맞는 코드를 작성해준다. 아직 베타버전이라서 그런지 여러가지 문제점(코드 작성중에 에러 남 ^^ 테스트 하고 계시는 코드덕후들이 상당할듯. 밥줄이 끊기게 되었으니... 닭 튀겨야 하나 ...)들이 많지만 그래도 상당하다.

아 , 물론 이것말고도 내가 알고 있는 코딩봇이 또 있다. 바로 git에서 만든 copilot 라는 녀석인데 유튜브 "코딩애플" 님의 숏츠 영상으로 처음 들었다. 음 개발자가 개발자를 팀킬하는 상황까지 왔다.

얘들아 아직도 코딩인강듣고있니? ^^

 

 

+ Recent posts