ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • spring boot jwt
    IT/spring 2020. 12. 25. 20:15

    spring boot jwt



    dependencies {
    compile group: 'io.jsonwebtoken', name: 'jjwt', version: '0.7.0'
    compile 'io.jsonwebtoken:jjwt-api:0.10.7'
    runtime 'io.jsonwebtoken:jjwt-impl:0.10.7'
    runtime 'io.jsonwebtoken:jjwt-jackson:0.10.7'
    }




    application.yaml


    project:
    jwt:
    secretKey: tototototototoknesesesesecrtkeyekekrkrk
    tokenPrefix: "Bearer "
    tokenExpirationAfterDays: 10



    @ToString
    @Getter
    @Setter
    @AllArgsConstructor
    @EqualsAndHashCode(callSuper = false)
    @Component
    @Configuration
    @EnableConfigurationProperties
    @ConfigurationProperties(prefix = "project.jwt")

    public class JwtConfig {

    private String secretKey;
    private String tokenPrefix;
    private Long tokenExpirationAfterDays;

    public JwtConfig() {
    }

    }


    @Configuration
    public class JwtSecretKey {

    private final JwtConfig jwtConfig;

    @Autowired
    public JwtSecretKey(JwtConfig jwtConfig) {
    this.jwtConfig = jwtConfig;
    }
    }



    public class JwtTokenVerifier extends OncePerRequestFilter {

    private final JwtConfig jwtConfig;
    private final CacheManager cacheManager;
    private final TokenService tokenService;

    public JwtTokenVerifier(JwtConfig jwtConfig, CacheManager cacheManager, TokenService tokenService) {
    this.jwtConfig = jwtConfig;
    this.cacheManager = cacheManager;
    this.tokenService = tokenService;

    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
    HttpServletResponse response,
    FilterChain filterChain) throws ServletException, IOException {

    String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);

    if (Strings.isNullOrEmpty(authorizationHeader) || !authorizationHeader.startsWith(jwtConfig.getTokenPrefix())) {
    filterChain.doFilter(request, response);
    return;
    }

    try {
    Jws<Claims> claimsJws = tokenService.parserJwt(authorizationHeader);
    Claims body = claimsJws.getBody();
    String username = body.getSubject();

    UserAuthToken userAuthToken = tokenService.getToken(UserAuthToken.builder().name(username).build());
    if (!authorizationHeader.equals(userAuthToken.getToken())){
    throw new JwtException("cache not matching token");
    }
    List<Map<String, String>> authorities = (List<Map<String, String>>) body.get("authorities");

    Set<SimpleGrantedAuthority> simpleGrantedAuthorities = authorities.stream()
    .map(m -> new SimpleGrantedAuthority(m.get("authority")))
    .collect(Collectors.toSet());

    Authentication authentication = new UsernamePasswordAuthenticationToken(
    username,
    null,
    simpleGrantedAuthorities
    );

    SecurityContextHolder.getContext().setAuthentication(authentication);

    } catch (JwtException e) {
    throw new IllegalStateException(String.format("Token %s cannot be trusted", authorizationHeader));
    }

    filterChain.doFilter(request, response);
    }
    }



    public class JwtUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;
    private final JwtConfig jwtConfig;
    private final UserRepository userRepository;
    public SecretKey secretKey;
    public static final String JWT_TOKEN_PROCESSES_URL = WebSecurityConfigurerAdapter.SECURITY_PATH + "/user-sign-in";
    private final TokenService tokenService;
    private final CacheManager cacheManager;

    public JwtUsernameAndPasswordAuthenticationFilter(AuthenticationManager authenticationManager,
    JwtConfig jwtConfig,
    CacheManager cacheManager,
    TokenService tokenService,
    UserRepository userRepository) {
    this.userRepository = userRepository;
    this.cacheManager = cacheManager;
    this.tokenService = tokenService;
    this.authenticationManager = authenticationManager;
    this.jwtConfig = jwtConfig;
    setFilterProcessesUrl(JWT_TOKEN_PROCESSES_URL);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
    HttpServletResponse response) throws AuthenticationException {

    try {
    UsernameAndPasswordAuthenticationRequest authenticationRequest = new ObjectMapper()
    .readValue(request.getInputStream(), UsernameAndPasswordAuthenticationRequest.class);

    String generatorPassword = userRepository.generatorPassword(authenticationRequest.getPassword());
    authenticationRequest.setPassword(generatorPassword);
    Authentication authentication = new UsernamePasswordAuthenticationToken(
    authenticationRequest.getUsername(),
    authenticationRequest.getPassword()
    );
    Authentication authenticate = authenticationManager.authenticate(authentication);

    return authenticate;

    } catch (IOException e) {
    throw new RuntimeException(e);
    }

    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
    HttpServletResponse response,
    FilterChain chain,
    Authentication authResult) throws IOException, ServletException {


    String token = tokenService.makeToken(authResult.getName(), authResult.getAuthorities());
    String fullToken = jwtConfig.getTokenPrefix() + token;
    response.addHeader(HttpHeaders.AUTHORIZATION, fullToken);

    UserAuthToken userAuthToken = UserAuthToken.builder().name(authResult.getName()).refreshToken(tokenService.makeRefreshToken()).token(fullToken).build();
    tokenService.putToken(userAuthToken);


    ObjectMapper objectMapper = new ObjectMapper();
    response.getWriter().write(objectMapper.writeValueAsString(userAuthToken));

    }
    }


    @ToString
    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    @EqualsAndHashCode(callSuper = false)
    public class UsernameAndPasswordAuthenticationRequest {

    private String username;
    private String password;

    }



    @Slf4j
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfigurerAdapter extends org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter {


    @Value("${project.properties.username}")
    String username;

    @Value("${project.properties.password}")
    String password;

    public static final String SECURITY_PATH = "/securitys";
    public static final String LOGOUT_URL = SECURITY_PATH+"/sign-out";
    public static final String LOGIN_PAGE = "/#/login";
    public static final String LOGIN_PROCESSING_URL = SECURITY_PATH+"/sign-in";
    public static final String USERNAME_PARAMETER = "username";
    public static final String USERPWD_PARAMETER = "password";
    public static final String DEFAULT_SUCCESS_URL = "/";


    private final PasswordEncoder passwordEncoder;
    private final UserDetailsService applicationUserService;
    private final UserRepository userRepository;
    private final JwtConfig jwtConfig;
    private final CacheManager cacheManager;
    private final TokenService tokenService;

    @Autowired
    public WebSecurityConfigurerAdapter(PasswordEncoder passwordEncoder,
    UserDetailsService applicationUserService,
    JwtConfig jwtConfig,
    CacheManager cacheManager,
    TokenService tokenService,
    UserRepository userRepository
    ) {
    this.userRepository = userRepository;
    this.cacheManager = cacheManager;
    this.tokenService = tokenService;
    this.passwordEncoder = passwordEncoder;
    this.applicationUserService = applicationUserService;
    this.jwtConfig = jwtConfig;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers(
    "/assets/**",
    "/*.css",
    "/*.js",
    "/*.map",
    "/*.ico",
    SecuritysController.URI_PREFIX+"/refresh"
    );
    }
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    http
    .csrf().disable()
    .sessionManagement()
    .and()
    .formLogin()
    .loginPage(LOGIN_PAGE) //로그인 페이지
    .loginProcessingUrl(LOGIN_PROCESSING_URL) //login-processing-url 로그인 페이지 form action에 입력할 주소 지정
    .usernameParameter(USERNAME_PARAMETER)
    .passwordParameter(USERPWD_PARAMETER)
    .defaultSuccessUrl(DEFAULT_SUCCESS_URL) //성공시 이동될 페이지
    .permitAll()
    .and()
    .logout()
    .logoutUrl(LOGOUT_URL)
    .invalidateHttpSession(true)
    .logoutSuccessUrl(DEFAULT_SUCCESS_URL)
    .and()
    .addFilter(new JwtUsernameAndPasswordAuthenticationFilter(authenticationManager(), jwtConfig, cacheManager, tokenService, userRepository))
    .addFilterAfter(new JwtTokenVerifier(jwtConfig, cacheManager, tokenService),JwtUsernameAndPasswordAuthenticationFilter.class)
    .authorizeRequests()
    .antMatchers("/", "index","/favicon.ico", "*.css","/css/*", "/js/*").permitAll()
    .antMatchers("/apis" ,"/apis/**").hasAnyRole(UserRole.USER.name(), UserRole.ADMIN.name())
    .antMatchers("/swagger", "/swagger/**").hasRole(UserRole.SWAGGER.name())
    .antMatchers("/docs", "/docs/**").hasRole(UserRole.DOCS.name())
    .anyRequest()
    .authenticated();

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(daoAuthenticationProvider());
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
    DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    provider.setPasswordEncoder(passwordEncoder);
    provider.setUserDetailsService(applicationUserService);
    return provider;
    }


    }



    @Service
    public class TokenService {
    public final static String TOKEN_KEY = "api-user-tokens";



    @Autowired
    public JwtConfig jwtConfig;

    @Cacheable(cacheNames = TOKEN_KEY, key="#userAuthToken.name")
    public UserAuthToken getToken(UserAuthToken userAuthToken){
    return userAuthToken;
    }

    @CachePut(cacheNames = TOKEN_KEY, key="#userAuthToken.name")
    public UserAuthToken putToken(UserAuthToken userAuthToken){
    return userAuthToken;
    }

    @CacheEvict(cacheNames = TOKEN_KEY, key="#userAuthToken.name")
    public void evictToken(UserAuthToken userAuthToken){
    }

    public Jws<Claims> parserJwt(String header) throws JwtException {
    String token = header.replace(jwtConfig.getTokenPrefix(), "");
    Jws<Claims> claimsJws = Jwts.parser()
    .setSigningKey(this.jwtConfig.getSecretKey().getBytes(StandardCharsets.UTF_8))
    .parseClaimsJws(token);
    return claimsJws;
    }

    public String makeToken(String subject, Collection<? extends GrantedAuthority> bodyAuthorities) {
    Date now = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());
    LocalDateTime localDateTime = LocalDateTime.now().plusDays(jwtConfig.getTokenExpirationAfterDays());
    Date expirationDate = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());


    String token = Jwts.builder()
    .setSubject(subject)
    .claim("authorities", bodyAuthorities)
    .setIssuedAt(now)
    .setExpiration(expirationDate)
    .signWith(SignatureAlgorithm.HS512, this.jwtConfig.getSecretKey().getBytes(StandardCharsets.UTF_8))
    .compact();
    return token;
    }

    public String makeRefreshToken() {
    String id = UUID.randomUUID().toString();
    Base64.Encoder encoder = Base64.getEncoder();
    String refreshtoken = new String(encoder.encode(id.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
    return refreshtoken;
    }

    }


    refresh token


    @Slf4j
    @RestController
    @RequestMapping(SecuritysController.URI_PREFIX)
    public class SecuritysController {
    public static final String URI_PREFIX = "/securitys";

    @Autowired
    CacheManager cacheManager;
    @Autowired
    TokenService tokenService;
    @Autowired
    JwtConfig jwtConfig;

    @Autowired
    UserRepository userRepository;

    @GetMapping(value = {"", "/"})
    public String securitys() {
    return "securitys";
    }

    @PostMapping(value = "/refresh")
    public UserAuthToken refresh(
    HttpServletRequest request,
    HttpServletResponse response,
    @RequestHeader(value= HttpHeaders.AUTHORIZATION) String authorization_header,
    @RequestBody UserRefreshToken refreshToken) {

    String name = null;
    try {
    Jws<Claims> claimsJws = tokenService.parserJwt(authorization_header);
    name = claimsJws.getBody().getSubject();
    } catch (ClaimJwtException e) { // ExpiredJwtException
    name = e.getClaims().getSubject();
    }

    // cache에 있는 값이랑 비교 token, refreshToken
    UserAuthToken userAuthToken = tokenService.getToken(UserAuthToken.builder().name(name).build());
    if (authorization_header.equals(userAuthToken.getToken()) && null != refreshToken && refreshToken.getRefreshToken().equals(userAuthToken.getRefreshToken())) {

    Optional<User> userOption = userRepository.findByNo(Long.parseLong(name));
    User user = userOption.orElseThrow(() -> new ErrorMsgException(new Error(MsgCode.E10003)));

    String token = tokenService.makeToken(Long.toString(user.getNo()), user.getRoles());
    String fullToken = jwtConfig.getTokenPrefix() + token;
    response.addHeader(HttpHeaders.AUTHORIZATION, fullToken);

    UserAuthToken newUserAuthToken = UserAuthToken.builder().name(Long.toString(user.getNo())).refreshToken(tokenService.makeRefreshToken()).token(fullToken).build();
    tokenService.putToken(newUserAuthToken);
    return newUserAuthToken;
    } else {
    throw new ErrorMsgException(new Error(MsgCode.E10003));
    }
    }


    }



    https://github.com/visualkhh/lib-spring/tree/master/boot/security-redis-jpa-restdoc-swagger-jwt-email-angular-thymeleaf-multi


    visualkhh@gmail.com

    감사합니다.



    댓글

Designed by Tistory.