Skip to content

Commit 8d8bb41

Browse files
docs: improve client sample codes to reflect Introspector changes
1 parent beeda10 commit 8d8bb41

File tree

10 files changed

+137
-30
lines changed

10 files changed

+137
-30
lines changed

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/response/error/exception/auth/CustomAuthGuardException.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.patternknife.securityhelper.oauth2.client.config.response.error.exception.auth;
22

3-
import org.springframework.security.access.AccessDeniedException;
3+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthorizationException;
44

5-
public class CustomAuthGuardException extends AccessDeniedException {
5+
public class CustomAuthGuardException extends KnifeOauth2AuthorizationException {
66

77
public CustomAuthGuardException(String message) {
88
super(message);

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/securityimpl/guard/AccessTokenUserInfo.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.springframework.security.core.GrantedAuthority;
44
import org.springframework.security.core.userdetails.User;
5+
import org.springframework.security.core.userdetails.UserDetails;
56
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
67

78
import java.util.Collection;
@@ -12,9 +13,16 @@
1213

1314

1415
/*
15-
* If you are using another Resource server, this class must also be located in com.patternknife.securityhelper.oauth2.interestedtreatmentpart.dto.security.
16+
*
17+
* AccessTokenUserInfo, which implements both UserDetails and OAuth2AuthenticatedPrincipal.
18+
* You can understand the reasoning behind this by reviewing AccessTokenUserInfoResolver and CustomResourceServerTokenIntrospector.
19+
*
20+
* If you are using a different Resource Server while setting patternknife.securityhelper.oauth2.introspection.type=database in application.properties, this class must also be located in the com.patternknife.securityhelper.oauth2.interestedtreatmentpart.dto.security package due to the token generation algorithm.
21+
*
22+
* I have not included this to the library.
23+
*
1624
* */
17-
public class AccessTokenUserInfo extends User implements OAuth2AuthenticatedPrincipal
25+
public class AccessTokenUserInfo extends User implements OAuth2AuthenticatedPrincipal, UserDetails
1826
{
1927
public AccessTokenUserInfo(String username, String password, Collection<? extends GrantedAuthority> authorities) {
2028
super(username, password, authorities);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard;
2+
3+
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage;
4+
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
5+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.KnifeErrorMessages;
6+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthenticationException;
7+
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl;
8+
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.userdetail.ConditionalDetailsService;
9+
10+
11+
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
12+
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal;
13+
14+
public class AccessTokenUserInfoConverter {
15+
16+
public static AccessTokenUserInfo from(Object principal,
17+
ConditionalDetailsService conditionalDetailsService,
18+
OAuth2AuthorizationServiceImpl authorizationService, ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService) {
19+
20+
AccessTokenUserInfo accessTokenUserInfo;
21+
if (principal instanceof AccessTokenUserInfo) {
22+
return ((AccessTokenUserInfo) principal);
23+
} else if (principal instanceof OAuth2IntrospectionAuthenticatedPrincipal) {
24+
String userName = ((OAuth2IntrospectionAuthenticatedPrincipal) principal).getUsername();
25+
String clientId = ((OAuth2IntrospectionAuthenticatedPrincipal) principal).getClientId();
26+
String appToken = ((OAuth2IntrospectionAuthenticatedPrincipal) principal).getAttribute("App-Token");
27+
28+
OAuth2Authorization oAuth2Authorization = authorizationService.findByUserNameAndClientIdAndAppToken(userName, clientId, appToken);
29+
if (oAuth2Authorization == null) {
30+
throw new KnifeOauth2AuthenticationException(iSecurityUserExceptionMessageService.getUserMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_LOGIN_FAILURE));
31+
}
32+
33+
return (AccessTokenUserInfo) conditionalDetailsService.loadUserByUsername(userName, clientId);
34+
}else {
35+
throw new KnifeOauth2AuthenticationException(KnifeErrorMessages.builder().message("Wrong principal : " + principal.toString()).userMessage(DefaultSecurityUserExceptionMessage.AUTHENTICATION_TOKEN_ERROR.getMessage()).build());
36+
}
37+
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Target(ElementType.PARAMETER)
9+
@Retention(RetentionPolicy.RUNTIME)
10+
public @interface CustomAuthenticationPrincipal {
11+
}

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/securityimpl/guard/UserCustomerOnlyImpl.java

+9-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard;
22

33
import com.patternknife.securityhelper.oauth2.client.config.response.error.exception.auth.CustomAuthGuardException;
4+
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.DefaultSecurityUserExceptionMessage;
5+
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
6+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.dto.KnifeErrorMessages;
7+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthenticationException;
8+
import io.github.patternknife.securityhelper.oauth2.api.config.security.response.error.exception.KnifeOauth2AuthorizationException;
49
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl;
510
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.userdetail.ConditionalDetailsService;
611
import lombok.RequiredArgsConstructor;
@@ -23,29 +28,17 @@ public class UserCustomerOnlyImpl {
2328

2429
private final OAuth2AuthorizationServiceImpl authorizationService;
2530
private final ConditionalDetailsService conditionalDetailsService;
31+
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;
2632

2733
@Around("@annotation(com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard.UserCustomerOnly)")
2834
public Object check(ProceedingJoinPoint joinPoint) throws Throwable {
2935

3036
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
31-
AccessTokenUserInfo accessTokenUserInfo;
32-
if (principal instanceof AccessTokenUserInfo) {
33-
accessTokenUserInfo = ((AccessTokenUserInfo) principal);
34-
}else {
35-
String userName = ((OAuth2IntrospectionAuthenticatedPrincipal) principal).getUsername();
36-
String clientId = ((OAuth2IntrospectionAuthenticatedPrincipal) principal).getClientId();
37-
String appToken = ((OAuth2IntrospectionAuthenticatedPrincipal) principal).getAttribute("App-Token");
38-
39-
40-
OAuth2Authorization oAuth2Authorization = authorizationService.findByUserNameAndClientIdAndAppToken(userName, clientId, appToken);
41-
42-
UserDetails userDetails = conditionalDetailsService.loadUserByUsername(userName, clientId);
43-
44-
accessTokenUserInfo = ((AccessTokenUserInfo) userDetails);
45-
}
37+
AccessTokenUserInfo accessTokenUserInfo = AccessTokenUserInfoConverter.from(principal, conditionalDetailsService, authorizationService, iSecurityUserExceptionMessageService);
4638

4739
if(accessTokenUserInfo != null && (accessTokenUserInfo.getAdditionalAccessTokenUserInfo().getUserType() != AdditionalAccessTokenUserInfo.UserType.CUSTOMER)){
48-
throw new CustomAuthGuardException("ID \"" + accessTokenUserInfo.getUsername() + "\" : Not in Customer Group");
40+
// Authorization
41+
throw new KnifeOauth2AuthorizationException("ID \"" + accessTokenUserInfo.getUsername() + "\" : Not in Customer Group");
4942
}
5043

5144
return joinPoint.proceed();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard.resolver;
2+
3+
4+
import com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard.CustomAuthenticationPrincipal;
5+
import com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard.AccessTokenUserInfoConverter;
6+
import io.github.patternknife.securityhelper.oauth2.api.config.security.message.ISecurityUserExceptionMessageService;
7+
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl;
8+
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.userdetail.ConditionalDetailsService;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.core.MethodParameter;
11+
import org.springframework.security.core.context.SecurityContextHolder;
12+
13+
import org.springframework.stereotype.Component;
14+
import org.springframework.web.bind.support.WebDataBinderFactory;
15+
import org.springframework.web.context.request.NativeWebRequest;
16+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
17+
import org.springframework.web.method.support.ModelAndViewContainer;
18+
19+
@Component
20+
@RequiredArgsConstructor
21+
public class AccessTokenUserInfoArgumentResolver implements HandlerMethodArgumentResolver {
22+
23+
private final OAuth2AuthorizationServiceImpl authorizationService;
24+
private final ConditionalDetailsService conditionalDetailsService;
25+
private final ISecurityUserExceptionMessageService iSecurityUserExceptionMessageService;
26+
27+
@Override
28+
public boolean supportsParameter(MethodParameter parameter) {
29+
return parameter.hasParameterAnnotation(CustomAuthenticationPrincipal.class);
30+
}
31+
32+
@Override
33+
public Object resolveArgument(MethodParameter parameter,
34+
ModelAndViewContainer mavContainer,
35+
NativeWebRequest webRequest,
36+
WebDataBinderFactory binderFactory) throws Exception {
37+
38+
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
39+
40+
return AccessTokenUserInfoConverter.from(principal, conditionalDetailsService, authorizationService, iSecurityUserExceptionMessageService);
41+
}
42+
}

client/src/main/java/com/patternknife/securityhelper/oauth2/client/config/securityimpl/serivce/userdetail/CustomerDetailsService.java

+4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ public void setEntityManager(EntityManager entityManager) {
5151
this.entityManager = entityManager;
5252
}
5353

54+
/*
55+
* Here, I return an instance of AccessTokenUserInfo, which implements both UserDetails and OAuth2AuthenticatedPrincipal.
56+
* You can understand the reasoning behind this by reviewing AccessTokenUserInfoResolver and CustomResourceServerTokenIntrospector.
57+
* */
5458
@Override
5559
public UserDetails loadUserByUsername(String username) {
5660

Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
package com.patternknife.securityhelper.oauth2.client.config.securityimpl.web;
22

33

4+
import com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard.resolver.AccessTokenUserInfoArgumentResolver;
45
import lombok.RequiredArgsConstructor;
56
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
68
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
79
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
810
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
911

12+
import java.util.List;
13+
1014
@RequiredArgsConstructor
1115
@Configuration
1216
@EnableWebMvc
1317
public class WebMvcConfig implements WebMvcConfigurer {
1418

19+
private final AccessTokenUserInfoArgumentResolver accessTokenUserInfoArgumentResolver;
20+
21+
1522
@Override
1623
public void addResourceHandlers(ResourceHandlerRegistry registry) {
1724
registry.addResourceHandler("/docs/**").addResourceLocations("classpath:/static/docs/");
1825
}
1926

27+
@Override
28+
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers){
29+
resolvers.add(accessTokenUserInfoArgumentResolver);
30+
}
2031
}

client/src/main/java/com/patternknife/securityhelper/oauth2/client/domain/admin/api/AdminApi.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.patternknife.securityhelper.oauth2.client.domain.admin.api;
22

3+
import com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard.CustomAuthenticationPrincipal;
34
import io.github.patternknife.securityhelper.oauth2.api.config.security.serivce.persistence.authorization.OAuth2AuthorizationServiceImpl;
45
import com.patternknife.securityhelper.oauth2.client.config.response.error.exception.data.ResourceNotFoundException;
56
import com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard.AccessTokenUserInfo;
@@ -16,7 +17,6 @@
1617
import org.springframework.data.domain.Page;
1718
import org.springframework.http.ResponseEntity;
1819
import org.springframework.security.access.prepost.PreAuthorize;
19-
import org.springframework.security.core.annotation.AuthenticationPrincipal;
2020
import org.springframework.security.oauth2.core.OAuth2AccessToken;
2121
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
2222
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
@@ -41,7 +41,7 @@ public class AdminApi {
4141

4242
@PreAuthorize("@resourceServerAuthorityChecker.hasAnyAdminRole()")
4343
@GetMapping("/admins/me")
44-
public AdminDTO.CurrentOneWithSessionRemainingSecondsRes getAdminSelf(@AuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo,
44+
public AdminDTO.CurrentOneWithSessionRemainingSecondsRes getAdminSelf(@CustomAuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo,
4545
@RequestHeader("Authorization") String authorizationHeader) throws ResourceNotFoundException {
4646

4747
String token = authorizationHeader.substring("Bearer ".length());
@@ -103,7 +103,7 @@ public Page<AdminDTO.OneWithRoleIdsRes> getAdminList(@RequestParam(value = "skip
103103
@RequestParam(value = "adminSearchFilter", required = false) String adminSearchFilter,
104104
@RequestParam(value = "sorterValueFilter", required = false) String sorterValueFilter,
105105
@RequestParam(value = "dateRangeFilter", required = false) String dateRangeFilter,
106-
@AuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo)
106+
@CustomAuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo)
107107
throws JsonProcessingException, ResourceNotFoundException {
108108

109109
return adminService.findAdminsByPageRequest(skipPagination, pageNum, pageSize, adminSearchFilter, sorterValueFilter, dateRangeFilter, accessTokenUserInfo);
@@ -113,7 +113,7 @@ public Page<AdminDTO.OneWithRoleIdsRes> getAdminList(@RequestParam(value = "skip
113113

114114
@PreAuthorize("hasAuthority('SUPER_ADMIN')")
115115
@GetMapping("/admins/{id}")
116-
public ResponseEntity<Admin> getAdminById(@PathVariable(value = "id") Long adminId, @AuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo)
116+
public ResponseEntity<Admin> getAdminById(@PathVariable(value = "id") Long adminId, @CustomAuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo)
117117
throws ResourceNotFoundException {
118118

119119
Admin adminDTO = adminService.findById(adminId);

client/src/main/java/com/patternknife/securityhelper/oauth2/client/domain/customer/api/CustomerApi.java

+5-6
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,18 @@
22

33

44
import com.patternknife.securityhelper.oauth2.client.config.response.error.exception.data.ResourceNotFoundException;
5+
import com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard.CustomAuthenticationPrincipal;
56
import com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard.UserCustomerOnly;
67
import com.patternknife.securityhelper.oauth2.client.config.securityimpl.guard.AccessTokenUserInfo;
78
import com.patternknife.securityhelper.oauth2.client.domain.customer.dao.CustomerRepository;
89
import com.patternknife.securityhelper.oauth2.client.domain.customer.dto.CustomerReqDTO;
910
import com.patternknife.securityhelper.oauth2.client.domain.customer.dto.CustomerResDTO;
1011
import com.patternknife.securityhelper.oauth2.client.domain.customer.service.CustomerService;
1112
import com.patternknife.securityhelper.oauth2.client.util.CustomUtils;
12-
import jakarta.annotation.Nullable;
1313
import jakarta.servlet.http.HttpServletRequest;
1414
import jakarta.validation.Valid;
1515
import lombok.AllArgsConstructor;
1616
import org.springframework.security.access.prepost.PreAuthorize;
17-
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1817
import org.springframework.security.oauth2.core.OAuth2AccessToken;
1918
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
2019
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
@@ -67,7 +66,7 @@ public CustomerResDTO.IdNameWithAccessTokenRemainingSeconds getCustomerSelf(
6766
@UserCustomerOnly
6867
@PreAuthorize("isAuthenticated()")
6968
@PatchMapping("/customers/me/delete")
70-
public CustomerResDTO.Id deleteMe(@AuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo) {
69+
public CustomerResDTO.Id deleteMe(@CustomAuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo) {
7170

7271
customerService.deleteCustomer(accessTokenUserInfo);
7372

@@ -103,7 +102,7 @@ public Map<String, Boolean> logoutCustomer(HttpServletRequest request) {
103102

104103
@PreAuthorize("@resourceServerAuthorityChecker.hasRole('CUSTOMER_ADMIN')")
105104
@GetMapping("/customers/{id}")
106-
public CustomerResDTO.Id getCustomerForAuthorizationTest(@PathVariable("id") final long id)
105+
public CustomerResDTO.Id getCustomerForAuthorizationTest(@PathVariable("id") final long id, @CustomAuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo)
107106
throws ResourceNotFoundException {
108107
return new CustomerResDTO.Id(id);
109108
}
@@ -117,7 +116,7 @@ public CustomerResDTO.Id update(@PathVariable("id") final long id, @Valid @Reque
117116

118117
@PreAuthorize("@resourceServerAuthorityChecker.hasRole('CUSTOMER_ADMIN')")
119118
@PatchMapping("/customers/{id}/delete")
120-
public CustomerResDTO.IdAdminId deleteCustomer(@PathVariable final long id, @AuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo) {
119+
public CustomerResDTO.IdAdminId deleteCustomer(@PathVariable final long id, @CustomAuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo) {
121120

122121
customerService.deleteCustomer(id, accessTokenUserInfo.getAdditionalAccessTokenUserInfo().getId());
123122

@@ -126,7 +125,7 @@ public CustomerResDTO.IdAdminId deleteCustomer(@PathVariable final long id, @Aut
126125

127126
@PreAuthorize("@resourceServerAuthorityChecker.hasRole('CUSTOMER_ADMIN')")
128127
@PatchMapping("/customers/{id}/restore")
129-
public CustomerResDTO.IdAdminId restoreCustomer(@PathVariable final long id, @AuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo) {
128+
public CustomerResDTO.IdAdminId restoreCustomer(@PathVariable final long id, @CustomAuthenticationPrincipal AccessTokenUserInfo accessTokenUserInfo) {
130129

131130
customerService.restoreCustomer(id);
132131

0 commit comments

Comments
 (0)