From 94227fdd37b175b701b6a83c572f5e1a2219501e Mon Sep 17 00:00:00 2001 From: Elex Date: Sat, 7 Aug 2021 19:56:41 +0900 Subject: [PATCH] 2021-08-07 --- security-with-jwt/build.gradle.kts | 7 ++ .../java/kr/pe/elex/examples/Application.java | 45 ++++++++ .../java/kr/pe/elex/examples/JwtFilter.java | 95 ++++++++++++++++ .../java/kr/pe/elex/examples/JwtService.java | 91 ++++++++++++++++ .../kr/pe/elex/examples/MyController.java | 71 ++++++++++++ .../kr/pe/elex/examples/MyRestController.java | 55 ++++++++++ .../kr/pe/elex/examples/SecurityConfig.java | 101 ++++++++++++++++++ .../java/kr/pe/elex/examples/UserService.java | 38 +++++++ .../java/kr/pe/elex/examples/WebConfig.java | 29 +++++ .../java/kr/pe/elex/examples/model/User.java | 76 +++++++++++++ .../elex/examples/model/UserRepository.java | 16 +++ .../kr/pe/elex/examples/package-info.java | 8 ++ .../src/main/resources/application.yaml | 17 +++ .../src/main/resources/banner.txt | 10 ++ .../src/main/resources/logback-spring.xml | 43 ++++++++ .../main/resources/templates/home.mustache | 15 +++ .../main/resources/templates/links.mustache | 7 ++ .../main/resources/templates/login.mustache | 11 ++ .../resources/templates/normal_info.mustache | 3 + .../resources/templates/secure_info.mustache | 4 + 20 files changed, 742 insertions(+) create mode 100644 security-with-jwt/src/main/java/kr/pe/elex/examples/Application.java create mode 100644 security-with-jwt/src/main/java/kr/pe/elex/examples/JwtFilter.java create mode 100644 security-with-jwt/src/main/java/kr/pe/elex/examples/JwtService.java create mode 100644 security-with-jwt/src/main/java/kr/pe/elex/examples/MyController.java create mode 100644 security-with-jwt/src/main/java/kr/pe/elex/examples/MyRestController.java create mode 100644 security-with-jwt/src/main/java/kr/pe/elex/examples/SecurityConfig.java create mode 100644 security-with-jwt/src/main/java/kr/pe/elex/examples/UserService.java create mode 100644 security-with-jwt/src/main/java/kr/pe/elex/examples/WebConfig.java create mode 100644 security-with-jwt/src/main/java/kr/pe/elex/examples/model/User.java create mode 100644 security-with-jwt/src/main/java/kr/pe/elex/examples/model/UserRepository.java create mode 100644 security-with-jwt/src/main/java/kr/pe/elex/examples/package-info.java create mode 100644 security-with-jwt/src/main/resources/application.yaml create mode 100644 security-with-jwt/src/main/resources/banner.txt create mode 100644 security-with-jwt/src/main/resources/logback-spring.xml create mode 100644 security-with-jwt/src/main/resources/templates/home.mustache create mode 100644 security-with-jwt/src/main/resources/templates/links.mustache create mode 100644 security-with-jwt/src/main/resources/templates/login.mustache create mode 100644 security-with-jwt/src/main/resources/templates/normal_info.mustache create mode 100644 security-with-jwt/src/main/resources/templates/secure_info.mustache diff --git a/security-with-jwt/build.gradle.kts b/security-with-jwt/build.gradle.kts index 9c6d3ba..adfc72c 100644 --- a/security-with-jwt/build.gradle.kts +++ b/security-with-jwt/build.gradle.kts @@ -13,6 +13,13 @@ plugins { } dependencies { + // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api + implementation("io.jsonwebtoken:jjwt-api:0.11.2") + + // https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl + runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.2") + runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.2") + implementation("org.springframework.boot:spring-boot-starter-mustache") implementation("org.springframework.boot:spring-boot-starter-web") diff --git a/security-with-jwt/src/main/java/kr/pe/elex/examples/Application.java b/security-with-jwt/src/main/java/kr/pe/elex/examples/Application.java new file mode 100644 index 0000000..e8a7668 --- /dev/null +++ b/security-with-jwt/src/main/java/kr/pe/elex/examples/Application.java @@ -0,0 +1,45 @@ +/* + * Spring-boot Examples + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; + +import kr.pe.elex.examples.model.User; +import kr.pe.elex.examples.model.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +@SpringBootApplication +public class Application { + + @Autowired + private UserRepository repository; + @Autowired + private PasswordEncoder passwordEncoder; + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + /** + * 실제 프로젝트에서는 입력 폼을 통해서 회원 가입을 하겠지만, ... + * + */ + @Component + public class SetupUsers implements CommandLineRunner { + + @Override + public void run(String... args) throws Exception { + User user = new User("Charlie", "user1", passwordEncoder.encode("pw1")); + repository.save(user); + } + + } +} diff --git a/security-with-jwt/src/main/java/kr/pe/elex/examples/JwtFilter.java b/security-with-jwt/src/main/java/kr/pe/elex/examples/JwtFilter.java new file mode 100644 index 0000000..f07dd7c --- /dev/null +++ b/security-with-jwt/src/main/java/kr/pe/elex/examples/JwtFilter.java @@ -0,0 +1,95 @@ +package kr.pe.elex.examples; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.SignatureException; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.util.MimeTypeUtils; +import org.springframework.web.filter.GenericFilterBean; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Slf4j +@Component +public class JwtFilter extends GenericFilterBean { + + @Autowired + private JwtService jwtService; + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + final HttpServletRequest httpServletRequest = (HttpServletRequest) request; + final String authHeader = httpServletRequest.getHeader("Authorization"); + if (null != authHeader) { + try { + Authentication authentication = jwtService.getAuthentication(authHeader); + SecurityContextHolder.getContext().setAuthentication(authentication); + + } catch (Exception e) { + handleException(e, response); + return; + } + } + + chain.doFilter(request, response); + } + + private void handleException(Exception e, ServletResponse response) { + final HttpServletResponse httpServletResponse = (HttpServletResponse) response; + final ErrorResponse error = new ErrorResponse(); + if (e instanceof IncorrectClaimException) { + error.setErrorCode(HttpServletResponse.SC_BAD_REQUEST); + error.setMessage(e.getMessage()); + } else if (e instanceof MissingClaimException) { + error.setErrorCode(HttpServletResponse.SC_BAD_REQUEST); + error.setMessage(e.getMessage()); + } else if (e instanceof ExpiredJwtException) { + error.setErrorCode(HttpServletResponse.SC_UNAUTHORIZED); + error.setMessage(e.getMessage()); + } else if (e instanceof SignatureException) { + error.setErrorCode(HttpServletResponse.SC_BAD_REQUEST); + error.setMessage(e.getMessage()); + } else if (e instanceof MalformedJwtException) { + error.setErrorCode(HttpServletResponse.SC_BAD_REQUEST); + error.setMessage(e.getMessage()); + } else if (e instanceof UnsupportedJwtException) { + error.setErrorCode(HttpServletResponse.SC_BAD_REQUEST); + error.setMessage(e.getMessage()); + } else { + error.setErrorCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + error.setMessage(e.getMessage()); + } + + try { + final ObjectMapper mapper = new ObjectMapper(); + httpServletResponse.setStatus(error.getErrorCode()); + httpServletResponse.setContentType(MimeTypeUtils.APPLICATION_JSON_VALUE); + httpServletResponse.getWriter().write(mapper.writeValueAsString(error)); + } catch (IOException ex) { + log.error("Oops~", ex); + } + } + + @Getter @Setter + private static class ErrorResponse { + @JsonProperty + private int errorCode; + @JsonProperty + private String message; + } +} diff --git a/security-with-jwt/src/main/java/kr/pe/elex/examples/JwtService.java b/security-with-jwt/src/main/java/kr/pe/elex/examples/JwtService.java new file mode 100644 index 0000000..9f0af90 --- /dev/null +++ b/security-with-jwt/src/main/java/kr/pe/elex/examples/JwtService.java @@ -0,0 +1,91 @@ +package kr.pe.elex.examples; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SignatureException; +import lombok.extern.slf4j.Slf4j; +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.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.*; + +@Slf4j +@Service +public class JwtService { + + private final byte[] key = new byte[32]; + + @PostConstruct + private void init() { + new Random().nextBytes(key); + log.info("JWT Key = {}", key); + } + + public String generateToken(final Authentication authentication) { + return Jwts.builder() + .setHeaderParam(Header.TYPE, Header.JWT_TYPE) + .setIssuer("Elex") + .setSubject(authentication.getName()) + .setExpiration(Date.from(Instant.now().plus(3, ChronoUnit.HOURS))) + .claim("authorities", authoritiesToString(authentication.getAuthorities())) + .signWith(Keys.hmacShaKeyFor(key)) + .compact(); + } + + public Jws parseToken(final String token) + throws UnsupportedJwtException, MalformedJwtException, SignatureException, ExpiredJwtException, + MissingClaimException, IncorrectClaimException { + + return Jwts.parserBuilder() + .setSigningKey(key) + .requireIssuer("Elex") // 토큰의 Issuer 일치 여부 확인 + .build() + .parseClaimsJws(parseHeader(token)); + } + + public Authentication getAuthentication(String token) + throws UnsupportedJwtException, MalformedJwtException, SignatureException, ExpiredJwtException, + MissingClaimException, IncorrectClaimException { + return getAuthentication(parseToken(token).getBody()); + } + + public Authentication getAuthentication(Claims claims) { + //User principal = (User) userService.loadUserByUsername(claims.getSubject()); + return new UsernamePasswordAuthenticationToken(claims.getSubject(), claims, + authoritiesFromString(claims.get("authorities", String.class))); + } + + private static String authoritiesToString(Collection authorities) { + StringJoiner joiner = new StringJoiner(","); + for (GrantedAuthority authority : authorities) { + joiner.add(authority.getAuthority()); + } + return joiner.toString(); + } + + private static Collection authoritiesFromString(String authorities) { + List list = new ArrayList<>(); + for (String authority : authorities.split(",")) { + list.add(new SimpleGrantedAuthority(authority)); + } + return list; + } + + private static String parseHeader(final String authorizationHeader) + throws MalformedJwtException { + final String[] authentication = authorizationHeader.split(" "); + if (authentication.length == 2 && authentication[0].matches("[bB]earer")) { + return authentication[1]; + } else if (authentication.length == 1) { + return authentication[0]; + } else { + throw new MalformedJwtException("Authentication Header param must be started with 'Bearer ': " + authorizationHeader); + } + } +} diff --git a/security-with-jwt/src/main/java/kr/pe/elex/examples/MyController.java b/security-with-jwt/src/main/java/kr/pe/elex/examples/MyController.java new file mode 100644 index 0000000..48618c4 --- /dev/null +++ b/security-with-jwt/src/main/java/kr/pe/elex/examples/MyController.java @@ -0,0 +1,71 @@ +/* + * Spring-boot Examples + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; + +import kr.pe.elex.examples.model.User; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +@Slf4j +@Controller +public class MyController { + + /** + * 로그인한 사용자 정보를 가져오려면, Authentication을 사용합니다. + * 또는, @AuthenticationPrincipal를 사용합니다. + * + * @param authentication + * @param model + * @return + */ + @GetMapping({"/"}) + public String index(Authentication authentication, Model model) { + if (null != authentication) { + User user = (User) (authentication.getPrincipal()); + log.info("USER: {}", user.getName()); + + model.addAttribute("user", user); + } else { + log.info("NO USER: "); + } + return "home"; + } + + @GetMapping({"/login"}) + public String login() { + + return "login"; + } + + @GetMapping({"/info"}) + public String info() { + return "normal_info"; + } + + /** + * 로그인한 사용자 정보를 가져오기 위해, @AuthenticationPrincipal를 사용합니다. + * + * @param user + * @param model + * @return + */ + @GetMapping({"/secure"}) + public String secure(@AuthenticationPrincipal User user, Model model) { + if (null != user) { + log.info("USER: {}", user.getName()); + } else { + log.info("NO USER!!"); + } + model.addAttribute("user", user); + return "secure_info"; + } +} diff --git a/security-with-jwt/src/main/java/kr/pe/elex/examples/MyRestController.java b/security-with-jwt/src/main/java/kr/pe/elex/examples/MyRestController.java new file mode 100644 index 0000000..0b4d57e --- /dev/null +++ b/security-with-jwt/src/main/java/kr/pe/elex/examples/MyRestController.java @@ -0,0 +1,55 @@ +package kr.pe.elex.examples; + +import kr.pe.elex.examples.model.User; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@RestController +@RequestMapping({"/api"}) +public class MyRestController { + @Autowired + private JwtService jwtService; + @Autowired + private UserService userService; + + @Autowired + private AuthenticationManager authenticationManager; + + @PostMapping("/signin") + public ResponseEntity signIn(@RequestParam String userId, @RequestParam String userPw) { + /* + 사용자 인증과정을 수동으로 진행 + */ + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(userId, userPw); + Authentication authentication = authenticationManager + .authenticate(authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + + /* + 인증 완료 후 토큰 발행 + */ + String token = jwtService.generateToken(authentication); + return ResponseEntity.ok() + .header("Authentication", "Bearer " + token) + .body(null); + } + + @GetMapping("/user/{userName}") + public ResponseEntity getUser(@PathVariable String userName) { + return ResponseEntity.ok((User) userService.loadUserByUsername(userName)); + } + + @ExceptionHandler(Throwable.class) + public ResponseEntity onException(Throwable e) { + log.error("ERROR!!!", e); + return ResponseEntity.internalServerError().body(e.getMessage()); + } +} diff --git a/security-with-jwt/src/main/java/kr/pe/elex/examples/SecurityConfig.java b/security-with-jwt/src/main/java/kr/pe/elex/examples/SecurityConfig.java new file mode 100644 index 0000000..9a37cd5 --- /dev/null +++ b/security-with-jwt/src/main/java/kr/pe/elex/examples/SecurityConfig.java @@ -0,0 +1,101 @@ +/* + * Spring-boot Examples + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; + +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +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.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private UserService userService; + @Autowired + private JwtFilter jwtFilter; + //@Autowired + //private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + + @Override + protected void configure(@NotNull HttpSecurity http) throws Exception { + http.authorizeRequests() + .antMatchers("/", "/info", "/h2-console").permitAll() // 아무나 접근 가능 + .antMatchers("/h2-console/**").permitAll() // H2콘솔을 쓰기 위해 추가했음 + .antMatchers("/api/signin").permitAll() + .antMatchers("/api/**").authenticated() + + //.antMatchers("/admin").hasAnyRole("ADMIN") + .anyRequest().authenticated() // 그 외에는 인증해야 접근 가능 + /*.and() + .formLogin()// 커스텀 로그인 폼 + .loginPage("/login") + .usernameParameter("user_id") // 로그인 폼 매개변수명 지정 + .passwordParameter("user_pw") + .permitAll() + .and() + .logout()// 로그아웃 + .logoutSuccessUrl("/") + .permitAll()*/ + //.and().httpBasic(); + .and().csrf().ignoringAntMatchers("/h2-console/**")// H2콘솔을 쓰기 위해 추가했음 + .and().headers().frameOptions().sameOrigin()// H2콘솔을 쓰기 위해 추가했음 + .and().csrf().ignoringAntMatchers("/api/**") + .and() + //.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint) + //.and() + .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ; + + } + + + @Override + protected void configure(@NotNull AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userService) + .passwordEncoder(passwordEncoder()) + /*auth.inMemoryAuthentication() + .withUser("user1") + .password("{noop}pw1") + .authorities("ROLE_USER") + .and() + .withUser("user2") + .password("{noop}pw2") + .authorities("ROLE_USER")*/ + ; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + /** + * 인증과정을 수동으로 진행하기 위해서 AuthenticationManager를 빈에 등록 시켜야 한다. + * @return + * @throws Exception + */ + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + +} diff --git a/security-with-jwt/src/main/java/kr/pe/elex/examples/UserService.java b/security-with-jwt/src/main/java/kr/pe/elex/examples/UserService.java new file mode 100644 index 0000000..d1e3ea8 --- /dev/null +++ b/security-with-jwt/src/main/java/kr/pe/elex/examples/UserService.java @@ -0,0 +1,38 @@ +/* + * Spring-boot Examples + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; + +import kr.pe.elex.examples.model.User; +import kr.pe.elex.examples.model.UserRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +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; + +/** + * UserDetailsService를 구현해서 사용자를 조회할 수 있는 서비스를 만듭니다. + */ +@Slf4j +@Service +public class UserService implements UserDetailsService { + @Autowired + private UserRepository repository; + + @Override + public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { + final User user = repository.findByUsername(username); + if (null == user) { + throw new UsernameNotFoundException("Unable to find a user with " + username); + } else { + return user; + } + } + +} diff --git a/security-with-jwt/src/main/java/kr/pe/elex/examples/WebConfig.java b/security-with-jwt/src/main/java/kr/pe/elex/examples/WebConfig.java new file mode 100644 index 0000000..a502280 --- /dev/null +++ b/security-with-jwt/src/main/java/kr/pe/elex/examples/WebConfig.java @@ -0,0 +1,29 @@ +/* + * Spring-boot Examples + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; + +import kr.pe.elex.examples.model.User; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Slf4j +@Configuration +public class WebConfig implements WebMvcConfigurer { + /* + @Override + public void addViewControllers(@NotNull ViewControllerRegistry registry) { + registry.addViewController("/login"); + } + */ + +} diff --git a/security-with-jwt/src/main/java/kr/pe/elex/examples/model/User.java b/security-with-jwt/src/main/java/kr/pe/elex/examples/model/User.java new file mode 100644 index 0000000..b263cc2 --- /dev/null +++ b/security-with-jwt/src/main/java/kr/pe/elex/examples/model/User.java @@ -0,0 +1,76 @@ +/* + * Spring-boot Examples + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples.model; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import javax.persistence.*; +import java.util.*; + +/** + * UserDetails를 구현해서 데이터 모델을 만듭니다. + */ +@Table(name = "User") +@Entity +@Getter +@Setter +@RequiredArgsConstructor +@ToString +public class User implements UserDetails { + @Column(nullable = false) + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String username; + private String password; + private String name; + private String role; + private boolean enabled; + + public User(String name, String username, String password){ + this(); + this.username = username; + this.password = password; + this.name = name; + this.role = "ROLE_USER"; + this.enabled = true; + + } + + @Override + public Collection getAuthorities() { + return Arrays.asList(new SimpleGrantedAuthority(role)); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return enabled; + } +} diff --git a/security-with-jwt/src/main/java/kr/pe/elex/examples/model/UserRepository.java b/security-with-jwt/src/main/java/kr/pe/elex/examples/model/UserRepository.java new file mode 100644 index 0000000..459bb2b --- /dev/null +++ b/security-with-jwt/src/main/java/kr/pe/elex/examples/model/UserRepository.java @@ -0,0 +1,16 @@ +/* + * Spring-boot Examples + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples.model; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRepository extends JpaRepository { + User findByUsername(String username); +} diff --git a/security-with-jwt/src/main/java/kr/pe/elex/examples/package-info.java b/security-with-jwt/src/main/java/kr/pe/elex/examples/package-info.java new file mode 100644 index 0000000..ce9cc62 --- /dev/null +++ b/security-with-jwt/src/main/java/kr/pe/elex/examples/package-info.java @@ -0,0 +1,8 @@ +/* + * Spring-boot Examples + * + * Copyright (c) 2021. Elex. All Rights Reserved. + * https://www.elex-project.com/ + */ + +package kr.pe.elex.examples; diff --git a/security-with-jwt/src/main/resources/application.yaml b/security-with-jwt/src/main/resources/application.yaml new file mode 100644 index 0000000..505f7d1 --- /dev/null +++ b/security-with-jwt/src/main/resources/application.yaml @@ -0,0 +1,17 @@ +spring: + application: + name: My spring-boot project + mustache: + expose-request-attributes: true # 뷰에서 CSRF를 뿌려주기 위해서 필요합니다. + h2: + console: + enabled: true + datasource: + url: jdbc:h2:mem:testdb + driverClassName: org.h2.Driver + username: sa + password: pw + jpa: + database-platform: org.hibernate.dialect.H2Dialect +server: + port: 8080 diff --git a/security-with-jwt/src/main/resources/banner.txt b/security-with-jwt/src/main/resources/banner.txt new file mode 100644 index 0000000..f7a35db --- /dev/null +++ b/security-with-jwt/src/main/resources/banner.txt @@ -0,0 +1,10 @@ + ('-. ('-. ) (`-. + _( OO) _( OO) ( OO ). +(,------.,--. (,------.(_/. \_)-. + | .---'| |.-') | .---' \ `.' / + | | | | OO ) | | \ /\ +(| '--. | |`-' |(| '--. \ \ | + | .--'(| '---.' | .--' .' \_) + | `---.| | | `---. / .'. \ + `------'`------' `------''--' '--' +powered by ELEX diff --git a/security-with-jwt/src/main/resources/logback-spring.xml b/security-with-jwt/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..d600313 --- /dev/null +++ b/security-with-jwt/src/main/resources/logback-spring.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + + + + + + UTF-8 + ${FILE_LOG_PATTERN} + + ${LOG_PATH} + + ${LOG_DIR}/sebastian_%d{yyyy-MM-dd}_%i.log.gz + + 10MB + + 60 + + + + + + + + diff --git a/security-with-jwt/src/main/resources/templates/home.mustache b/security-with-jwt/src/main/resources/templates/home.mustache new file mode 100644 index 0000000..642ccd7 --- /dev/null +++ b/security-with-jwt/src/main/resources/templates/home.mustache @@ -0,0 +1,15 @@ +

+

테스트 페이지입니다.

+{{#user}} + 안녕, {{name}}. + +
+ {{! CSRF 토큰이 없으면 에러 남. }} + + +
+{{/user}} +{{^user}} + +{{/user}} +{{> links}} diff --git a/security-with-jwt/src/main/resources/templates/links.mustache b/security-with-jwt/src/main/resources/templates/links.mustache new file mode 100644 index 0000000..f5601cb --- /dev/null +++ b/security-with-jwt/src/main/resources/templates/links.mustache @@ -0,0 +1,7 @@ +
+ | + 일반 | + 중요 | + 로그인 | + DB +
diff --git a/security-with-jwt/src/main/resources/templates/login.mustache b/security-with-jwt/src/main/resources/templates/login.mustache new file mode 100644 index 0000000..642875e --- /dev/null +++ b/security-with-jwt/src/main/resources/templates/login.mustache @@ -0,0 +1,11 @@ +

로그인

+
+ + + + + + +
+ +{{> links}} diff --git a/security-with-jwt/src/main/resources/templates/normal_info.mustache b/security-with-jwt/src/main/resources/templates/normal_info.mustache new file mode 100644 index 0000000..551c0cf --- /dev/null +++ b/security-with-jwt/src/main/resources/templates/normal_info.mustache @@ -0,0 +1,3 @@ +

아무나 볼 수 있는 페이지

+

This page doesn't contain any Important messages.

+{{> links}} diff --git a/security-with-jwt/src/main/resources/templates/secure_info.mustache b/security-with-jwt/src/main/resources/templates/secure_info.mustache new file mode 100644 index 0000000..0b3353a --- /dev/null +++ b/security-with-jwt/src/main/resources/templates/secure_info.mustache @@ -0,0 +1,4 @@ +

로그인해야 볼 수 있는 페이지

+

This page contains a Important message.

+{{#user}}Hello, {{name}}{{/user}} +{{> links}}