From 6bcbe72b43d6fa041d06878d1dae09a6d8903895 Mon Sep 17 00:00:00 2001 From: jack <jack.su@pollex.com.tw> Date: 星期五, 11 八月 2023 16:19:02 +0800 Subject: [PATCH] [ADD] 為了解決滲透測試JWT token 登出未失效問題, 在登出後會需要建立黑名單並在filter中確定token是否非黑名單 --- pamapi/src/main/java/com/pollex/pam/config/SecurityConfiguration.java | 1 pamapi/src/main/java/com/pollex/pam/web/rest/UserJWTController.java | 30 +++++++++++++++ pamapi/src/main/java/com/pollex/pam/security/jwt/TokenProvider.java | 16 ++++++++ pamapi/src/doc/sql/executed/20230727_j.sql | 0 pamapi/src/main/java/com/pollex/pam/security/jwt/JWTFilter.java | 24 +++++++++++ pamapi/src/main/java/com/pollex/pam/security/provider/EServiceAuthenticationProvider.java | 5 +- pamapi/src/doc/sql/20230803_j.sql | 7 +++ pamapi/src/doc/登出API/登出API.txt | 3 + 8 files changed, 83 insertions(+), 3 deletions(-) diff --git a/pamapi/src/doc/sql/20230803_j.sql b/pamapi/src/doc/sql/20230803_j.sql new file mode 100644 index 0000000..1aff561 --- /dev/null +++ b/pamapi/src/doc/sql/20230803_j.sql @@ -0,0 +1,7 @@ +-- �鈭摹暺��jwt token��敺��暺�霈oken憭望�� + +CREATE TABLE public.tokens_black_list ( + jwt_token text NOT NULL, + created_date timestamp NULL, + CONSTRAINT tokens_black_list_pkey PRIMARY KEY (jwt_token) +); \ No newline at end of file diff --git a/pamapi/src/doc/sql/20230727_j.sql b/pamapi/src/doc/sql/executed/20230727_j.sql similarity index 100% rename from pamapi/src/doc/sql/20230727_j.sql rename to pamapi/src/doc/sql/executed/20230727_j.sql diff --git "a/pamapi/src/doc/\347\231\273\345\207\272API/\347\231\273\345\207\272API.txt" "b/pamapi/src/doc/\347\231\273\345\207\272API/\347\231\273\345\207\272API.txt" new file mode 100644 index 0000000..11dadef --- /dev/null +++ "b/pamapi/src/doc/\347\231\273\345\207\272API/\347\231\273\345\207\272API.txt" @@ -0,0 +1,3 @@ +http post : + +http://localhost:8080/api/logout \ No newline at end of file diff --git a/pamapi/src/main/java/com/pollex/pam/config/SecurityConfiguration.java b/pamapi/src/main/java/com/pollex/pam/config/SecurityConfiguration.java index b9d3baf..191ed67 100644 --- a/pamapi/src/main/java/com/pollex/pam/config/SecurityConfiguration.java +++ b/pamapi/src/main/java/com/pollex/pam/config/SecurityConfiguration.java @@ -80,6 +80,7 @@ .authorizeRequests() .antMatchers("/api/access_analysis/**").permitAll() .antMatchers("/api/authenticate").permitAll() + .antMatchers("/api/logout").permitAll() .antMatchers("/api/register").permitAll() .antMatchers("/api/activate").permitAll() .antMatchers("/api/testLogin/**").permitAll() diff --git a/pamapi/src/main/java/com/pollex/pam/security/jwt/JWTFilter.java b/pamapi/src/main/java/com/pollex/pam/security/jwt/JWTFilter.java index a574659..7e3a0b9 100644 --- a/pamapi/src/main/java/com/pollex/pam/security/jwt/JWTFilter.java +++ b/pamapi/src/main/java/com/pollex/pam/security/jwt/JWTFilter.java @@ -1,15 +1,24 @@ package com.pollex.pam.security.jwt; import java.io.IOException; +import java.util.Optional; + 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 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.StringUtils; import org.springframework.web.filter.GenericFilterBean; + +import com.pollex.pam.business.domain.TokenBlackList; +import com.pollex.pam.business.repository.TokenBlackListRepository; /** * Filters incoming requests and installs a Spring Security principal if a header corresponding to a valid user is @@ -22,6 +31,7 @@ public static final String AUTHORIZATION_TOKEN = "access_token"; private final TokenProvider tokenProvider; + public JWTFilter(TokenProvider tokenProvider) { this.tokenProvider = tokenProvider; @@ -32,19 +42,31 @@ throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; String jwt = resolveToken(httpServletRequest); + if(StringUtils.hasText(jwt) && !jwt.equals("null")) { + boolean isBlackToken = this.tokenProvider.isBlackListToken(jwt); + if(isBlackToken) { + HttpServletResponse response = (HttpServletResponse) servletResponse; + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } + } + if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) { - Authentication authentication = this.tokenProvider.getAuthentication(jwt); + + Authentication authentication = this.tokenProvider.getAuthentication(jwt); SecurityContextHolder.getContext().setAuthentication(authentication); } + filterChain.doFilter(servletRequest, servletResponse); } private String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader(AUTHORIZATION_HEADER); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } String jwt = request.getParameter(AUTHORIZATION_TOKEN); + if (StringUtils.hasText(jwt)) { return jwt; } diff --git a/pamapi/src/main/java/com/pollex/pam/security/jwt/TokenProvider.java b/pamapi/src/main/java/com/pollex/pam/security/jwt/TokenProvider.java index 1986286..6cff94b 100644 --- a/pamapi/src/main/java/com/pollex/pam/security/jwt/TokenProvider.java +++ b/pamapi/src/main/java/com/pollex/pam/security/jwt/TokenProvider.java @@ -7,8 +7,12 @@ import java.security.Key; import java.util.*; import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletResponse; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -16,6 +20,10 @@ import org.springframework.security.core.userdetails.User; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; + +import com.pollex.pam.business.domain.TokenBlackList; +import com.pollex.pam.business.repository.TokenBlackListRepository; + import tech.jhipster.config.JHipsterProperties; @Component @@ -33,6 +41,9 @@ private final long tokenValidityInMilliseconds; private final long tokenValidityInMillisecondsForRememberMe; + + @Autowired + TokenBlackListRepository tokenBlackListRepository; public TokenProvider(JHipsterProperties jHipsterProperties) { byte[] keyBytes; @@ -102,4 +113,9 @@ } return false; } + + public boolean isBlackListToken(String jwt) { + Optional<TokenBlackList> tokenBlack = tokenBlackListRepository.findById(jwt); + return tokenBlack.isPresent(); + } } diff --git a/pamapi/src/main/java/com/pollex/pam/security/provider/EServiceAuthenticationProvider.java b/pamapi/src/main/java/com/pollex/pam/security/provider/EServiceAuthenticationProvider.java index f157b02..b5924f2 100644 --- a/pamapi/src/main/java/com/pollex/pam/security/provider/EServiceAuthenticationProvider.java +++ b/pamapi/src/main/java/com/pollex/pam/security/provider/EServiceAuthenticationProvider.java @@ -58,7 +58,8 @@ return getConsultantTokenAndRecordLoginTime(account, credentials); } else { - throw new EServiceErrorException(eServiceResponse.getMsg()); + log.debug("account:{},error:{}",account,eServiceResponse.getMsg()); + throw new EServiceErrorException("撣唾��Ⅳ�隤�"); } } @@ -69,7 +70,7 @@ } private UsernamePasswordAuthenticationToken getConsultantTokenAndRecordLoginTime(String account, String credential) throws ConsultantDisableException { - Consultant consultant = consultantRepository.findOneByAgentNo(account).orElseThrow(() -> new UsernameNotFoundException("閰脤“����蒂銝��慦�像�蝟餌絞銝�")); + Consultant consultant = consultantRepository.findOneByAgentNo(account).orElseThrow(() -> new UsernameNotFoundException("撣唾��Ⅳ�隤�")); List<GrantedAuthority> grantedAuths = Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(account, credential, grantedAuths); diff --git a/pamapi/src/main/java/com/pollex/pam/web/rest/UserJWTController.java b/pamapi/src/main/java/com/pollex/pam/web/rest/UserJWTController.java index 90c0be8..a0ce0eb 100644 --- a/pamapi/src/main/java/com/pollex/pam/web/rest/UserJWTController.java +++ b/pamapi/src/main/java/com/pollex/pam/web/rest/UserJWTController.java @@ -3,8 +3,15 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.pollex.pam.security.jwt.JWTFilter; import com.pollex.pam.security.jwt.TokenProvider; +import com.pollex.pam.business.domain.TokenBlackList; +import com.pollex.pam.business.repository.TokenBlackListRepository; import com.pollex.pam.business.web.vm.LoginVM; + +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -12,6 +19,7 @@ import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; /** @@ -24,6 +32,9 @@ private final TokenProvider tokenProvider; private final AuthenticationManagerBuilder authenticationManagerBuilder; + + @Autowired + TokenBlackListRepository tokenBlackListRepository; public UserJWTController(TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) { this.tokenProvider = tokenProvider; @@ -44,6 +55,25 @@ httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt); return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK); } + + @PostMapping("/logout") + public void logout(HttpServletRequest servletRequest) { + String jwtToken = resolveToken(servletRequest); + TokenBlackList blackList = new TokenBlackList(jwtToken); + tokenBlackListRepository.save(blackList); + } + + private String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader(JWTFilter.AUTHORIZATION_HEADER); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7); + } + String jwt = request.getParameter(JWTFilter.AUTHORIZATION_TOKEN); + if (StringUtils.hasText(jwt)) { + return jwt; + } + return null; + } /** * Object to return as body in JWT Authentication. -- Gitblit v1.8.0