public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
final boolean debug = logger.isDebugEnabled();
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
try {
Authentication authentication = tokenExtractor.extract(request);
...
catch (OAuth2Exception failed) {
SecurityContextHolder.clearContext();
if (debug) {
logger.debug("Authentication request failed: " + failed);
}
eventPublisher.publishAuthenticationFailure(new BadCredentialsException(failed.getMessage(), failed),
new PreAuthenticatedAuthenticationToken("access-token", "N/A"));
authenticationEntryPoint.commence(request, response,
new InsufficientAuthenticationException(failed.getMessage(), failed));
return;
}
chain.doFilter(request, response);
}
public class OAuth2AuthenticationProcessingFilter implements Filter, InitializingBean {
private final static Log logger = LogFactory.getLog(OAuth2AuthenticationProcessingFilter.class);
private AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
...
}
public class OAuth2AuthenticationEntryPoint extends AbstractOAuth2SecurityExceptionHandler implements
AuthenticationEntryPoint {
...
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
doHandle(request, response, authException);
}
...
}
protected final void doHandle(HttpServletRequest request, HttpServletResponse response, Exception authException)
throws IOException, ServletException {
try {
ResponseEntity> result = exceptionTranslator.translate(authException);
result = enhanceResponse(result, authException);
exceptionRenderer.handleHttpEntityResponse(result, new ServletWebRequest(request, response));
response.flushBuffer();
}
...
}
public class LLGAuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint {
@Autowired
private OAuth2ClientProperties oAuth2ClientProperties;
@Autowired
private BaseOAuth2ProtectedResourceDetails baseOAuth2ProtectedResourceDetails;
private WebResponseExceptionTranslator> exceptionTranslator = new DefaultWebResponseExceptionTranslator();
@Autowired
RestTemplate restTemplate;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
try {
//解析异常,如果是401则处理
ResponseEntity> result = exceptionTranslator.translate(authException);
if (result.getStatusCode() == HttpStatus.UNAUTHORIZED) {
MultiValueMap formData = new LinkedMultiValueMap();
formData.add("client_id", oAuth2ClientProperties.getClientId());
formData.add("client_secret", oAuth2ClientProperties.getClientSecret());
formData.add("grant_type", "refresh_token");
Cookie[] cookie=request.getCookies();
for(Cookie coo:cookie){
if(coo.getName().equals("refresh_token")){
formData.add("refresh_token", coo.getValue());
}
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
Map map = restTemplate.exchange(baseOAuth2ProtectedResourceDetails.getAccessTokenUri(), HttpMethod.POST,
new HttpEntity>(formData, headers), Map.class).getBody();
//如果刷新异常,则坐进一步处理
if(map.get("error")!=null){
// 返回指定格式的错误信息
response.setStatus(401);
response.setHeader("Content-Type", "application/json;charset=utf-8");
response.getWriter().print("{\"code\":1,\"message\":\""+map.get("error_description")+"\"}");
response.getWriter().flush();
//如果是网页,跳转到登陆页面
//response.sendRedirect("login");
}else{
//如果刷新成功则存储cookie并且跳转到原来需要访问的页面
for(Object key:map.keySet()){
response.addCookie(new Cookie(key.toString(),map.get(key).toString()));
}
request.getRequestDispatcher(request.getRequestURI()).forward(request,response);
}
}else{
//如果不是401异常,则以默认的方法继续处理其他异常
super.commence(request,response,authException);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public abstract class ResServerConfig extends ResourceServerConfigurerAdapter {
...
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
super.configure(resources);
resources.authenticationEntryPoint(new LLGAuthenticationEntryPoint());
}
PostMapping(value = "/login")
public ResponseEntity login(@RequestBody @Valid LoginDTO loginDTO, BindingResult bindingResult, HttpServletResponse response) throws Exception {
if (bindingResult.hasErrors()) {
throw new Exception("登录信息格式错误");
} else {
//Http Basic 验证
String clientAndSecret = oAuth2ClientProperties.getClientId() + ":" + oAuth2ClientProperties.getClientSecret();
//这里需要注意为 Basic 而非 Bearer
clientAndSecret = "Basic " + Base64.getEncoder().encodeToString(clientAndSecret.getBytes());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Authorization", clientAndSecret);
//授权请求信息
MultiValueMap map = new LinkedMultiValueMap<>();
map.put("username", Collections.singletonList(loginDTO.getUsername()));
map.put("password", Collections.singletonList(loginDTO.getPassword()));
map.put("grant_type", Collections.singletonList(oAuth2ProtectedResourceDetails.getGrantType()));
map.put("scope", oAuth2ProtectedResourceDetails.getScope());
//HttpEntity
HttpEntity httpEntity = new HttpEntity(map, httpHeaders);
//获取 Token
ResponseEntity body = restTemplate.exchange(oAuth2ProtectedResourceDetails.getAccessTokenUri(), HttpMethod.POST, httpEntity, OAuth2AccessToken.class);
OAuth2AccessToken oAuth2AccessToken = body.getBody();
response.addCookie(new Cookie("access_token", oAuth2AccessToken.getValue()));
response.addCookie(new Cookie("refresh_token", oAuth2AccessToken.getRefreshToken().getValue()));
return body;
}
}
本次由于对spring security oauth2了解不深入,导致在寻找异常抛出解决方法的时候折腾了一下,整体的思路并不复杂,只是用到了最普通的请求转发,但是需要对过滤器链有一定了解,打断点慢慢看是不错的选择。