我需要使用某些参数直接验证用户身份。如安全参考文档中所述,构造了有效的身份验证对象后,我将调用SecurityContextHolder.getContext()。setAuthentication(authentication)。这是我的代码:
@RequestMapping("/api/esia")
public class EsiaController {
@ResponseBody
@RequestMapping(value = "/auth/{id}", method = RequestMethod.GET)
public void auth(@PathVariable(name = "id") Long id,
HttpServletRequest request, HttpServletResponse response) throws IOException {//todo test
AtomicReference<String> token = new AtomicReference<>();
sysUsersRepository.findById(id).ifPresent(sysUsers -> {
token.set(esiaAuthenticateProvider.authenticateToContext(sysUsers, request));
});
customContextRepository.saveContext("contextByString", SecurityContextHolder.getContext());
response.sendRedirect("/test1/" + token.get());
}
@ResponseBody
@RequestMapping(value = "/test", method = RequestMethod.GET)
public User test(HttpServletRequest request, HttpServletResponse response) {//todo test
SecurityContext context = customContextRepository.getContext("contextByString");
if(context.getAuthentication() != SecurityContextHolder.getContext().getAuthentication()){
log.info("Contexts don't equals");
}
return SecurityContextHolder.getContext().getAuthentication() != null ?
(User) SecurityContextHolder.getContext().getAuthentication().getPrincipal() :
null;
}
};
@Service
public class CustomContextRepository {
private Map<String, SecurityContext> allContexts = new ConcurrentHashMap<>();
private Map<String, HttpSession> allSessions = new ConcurrentHashMap<>();
public void saveContext(String jsessionid, SecurityContext securityContext){
allContexts.put(jsessionid, securityContext);
}
public SecurityContext getContext(String jsessionid){
return allContexts.getOrDefault(jsessionid, null);
}
public void saveHttpSession(String jsessionid, HttpSession httpSession){
allSessions.put(jsessionid, httpSession);
}
public HttpSession getHttpSession(String jsessionid){
return allSessions.getOrDefault(jsessionid, null);
}
}
@Slf4j
@Service
@RequiredArgsConstructor
public class CustomEsiaAuthenticateProvider {
...
public String authenticateToContext(SysUsers sysUsers, HttpServletRequest httpRequest) {
//возможно понадобится
httpRequest.setAttribute("OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE", UUID.randomUUID().toString());
httpRequest.setAttribute("OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE", "Bearer");
AtomicReference<String> nextToken = new AtomicReference<>();
createUserBySysUser(sysUsers).ifPresent(user -> {
OAuth2AuthenticationDetails details = new OAuth2AuthenticationDetails(httpRequest);
Map<String, String> cridetials = new HashMap<>();
cridetials.put("username", user.getUsername());
cridetials.put("password", user.getPassword());
UsernamePasswordAuthenticationToken usernameToken =
new UsernamePasswordAuthenticationToken(user, cridetials, user.getAuthorities());
OAuth2Request customStoredRequest = createCustomStoredRequest(user.getUsername());
OAuth2Authentication authentication = new OAuth2Authentication(customStoredRequest, usernameToken);
authentication.setDetails(details);
usernameToken.setDetails(details);
authentication.setAuthenticated(true);
authentication.getPrincipal();
SecurityContext securityContext = SecurityContextHolder.getContext();
if (securityContext != null) {
securityContext.setAuthentication(authentication);
HttpSession session = httpRequest.getSession(true);
session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, securityContext);
nextToken.set(details.getTokenValue());
}
SecurityContextHolder.getContextHolderStrategy().setContext(securityContext);
});
return nextToken.get();
}
private OAuth2Request createCustomStoredRequest(String userLogin){...}
private Optional<User> createUserBySysUser(SysUsers sysUsers) {...}
}
当我发送用户ID为http://localhost:8080/api/esia/login/2的请求时,OAuth2Authentication已创建并保存在SecurityContextHolder中。我也从OAuth2AuthenticationDetails获取oauth2令牌值。收到令牌后,我将发送带有标头“ Authorization:Bearer token-value”的测试请求http://localhost:8080/api/esia/test,并获得http状态为401的响应。如果发送不带有认证标头的请求http://localhost:8080/api/esia/test,则用户被认证为匿名用户,但其中有我的主体(id = 2)的SecurityContext,之前已保存在customContextRepository中。我的猜测是SecurityContext会忽略我的呼叫setAuthentication或拒绝呼叫而不会出错。