在開始重構前我們來回憶下前面的流程
三種方式登陸成功後組裝後續流程返回token,必須攜帶basic client信息(因爲需要它獲取clientDetails信息)
ok分析完流程後我們進行重構。先直接用瀏覽器的配置進行修改後續繼續重構優化
登陸成功轉換token處理
package com.rui.tiger.auth.core.authentication;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rui.tiger.auth.core.model.enums.LoginTypeEnum;
import com.rui.tiger.auth.core.properties.SecurityProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.config.annotation.configuration.ClientDetailsServiceConfiguration;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Base64;
/**
* 認證成功處理器
* {@link SavedRequestAwareAuthenticationSuccessHandler}是Spring Security默認的成功處理器
*
* @author CaiRui
* @date 2018-12-6 12:39
*/
@Component("tigerAuthenticationSuccessHandler")
@Slf4j
public class TigerAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Autowired
private SecurityProperties securityProperties;
/**
* 授權服務器:自動配置的
* @see ClientDetailsServiceConfiguration#clientDetailsService()
*/
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private AuthorizationServerTokenServices authorizationServerTokenServices;
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
log.info("登錄成功");
/**
* @see BasicAuthenticationFilter#doFilterInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain)
* */
String header = request.getHeader("Authorization");
if (header == null || !header.startsWith("Basic ")) {
// 不被認可的客戶端異常
throw new UnapprovedClientAuthenticationException("沒有Authorization請求頭");
}
// 解析請Authorization 獲取client信息 client-id: tigerauth client-secret: 123456
String[] tokens = extractAndDecodeHeader(header, request);
assert tokens.length == 2;
String clientId = tokens[0];
String clientSecret = tokens[1];
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
// 判定提交的是否與查詢的匹配
if (clientDetails == null) {
throw new UnapprovedClientAuthenticationException("clientId對應的配置信息不存在:" + clientId);
} else if (!StringUtils.equals(clientDetails.getClientSecret(), clientSecret)) {
throw new UnapprovedClientAuthenticationException("clientSecret不匹配:" + clientId);
}
/** @see DefaultOAuth2RequestFactory#createTokenRequest(java.util.Map, org.springframework.security.oauth2.provider.ClientDetails)
* requestParameters,不同的授權模式有不同的參數,這裏自定義的模式,沒有參數
* String clientId,
* Collection<String> scope, 給自己的前段使用,默認用所有的即可
* String grantType 自定義 custom
* */
TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_SORTED_MAP, clientId, clientDetails.getScope(), "custom");
OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
/**
* @see org.springframework.security.oauth2.provider.token.AbstractTokenGranter#getOAuth2Authentication(org.springframework.security.oauth2.provider.ClientDetails, org.springframework.security.oauth2.provider.TokenRequest)
* */
OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
OAuth2AccessToken accessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
response.setContentType("application/json;charset=UTF-8");
/* log.info("TOKEN信息:" + JSON.toJSONString(accessToken));
response.getWriter().write(JSON.toJSONString(accessToken));*/
log.info("jack TOKEN信息:" + objectMapper.writeValueAsString(accessToken));
response.getWriter().write(objectMapper.writeValueAsString(accessToken));
}
/**
* Decodes the header into a username and password.
* @throws BadCredentialsException if the Basic header is not present or is not valid
* Base64
*/
private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException {
byte[] base64Token = header.substring(6).getBytes("UTF-8");
byte[] decoded;
try {
decoded = Base64.getDecoder().decode(base64Token);
} catch (IllegalArgumentException e) {
throw new BadCredentialsException(
"Failed to decode basic authentication token");
}
String token = new String(decoded, "UTF-8");
int delim = token.indexOf(":");
if (delim == -1) {
throw new BadCredentialsException("Invalid basic authentication token");
}
return new String[]{token.substring(0, delim), token.substring(delim + 1)};
}
}
權限配置器
package com.rui.tiger.auth.app;
import com.rui.tiger.auth.core.config.CaptchaSecurityConfig;
import com.rui.tiger.auth.core.config.SmsAuthenticationSecurityConfig;
import com.rui.tiger.auth.core.properties.SecurityConstants;
import com.rui.tiger.auth.core.properties.SecurityProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.social.security.SpringSocialConfigurer;
/**
* @author CaiRui
* @date 2019-04-17 08:38
*/
@Configuration
@EnableResourceServer
public class TigerResourceServerConfig extends ResourceServerConfigurerAdapter{
@Autowired
private AuthenticationSuccessHandler tigerAuthenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler tigerAuthenticationFailureHandler;
@Autowired
private SmsAuthenticationSecurityConfig smsAuthenticationSecurityConfig;//短信登陸配置
@Autowired
private SpringSocialConfigurer tigerSpringSocialConfigurer;
@Autowired
private SecurityProperties securityProperties;
@Override
public void configure(HttpSecurity http) throws Exception {
/**
* 表單密碼配置
*/
http.formLogin()
.loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)
.loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM)//
.defaultSuccessUrl("/index.html")
.successHandler(tigerAuthenticationSuccessHandler)
.failureHandler(tigerAuthenticationFailureHandler);
http
/*.apply(captchaSecurityConfig) //圖形驗證碼的有問題 先不處理
.and()*/
.apply(smsAuthenticationSecurityConfig)
.and()
.apply(tigerSpringSocialConfigurer)
.and()
.authorizeRequests()
.antMatchers(
SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,//權限認證
SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,//手機
securityProperties.getBrowser().getLoginPage(),//登錄頁面
SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*",// /captcha/* 驗證碼放行
securityProperties.getBrowser().getSignupUrl(),
//這個第三方自定義權限 後續抽離出去 可配置
securityProperties.getBrowser().getLoginOut(),
"/user/regist",
"/index.html",
securityProperties.getBrowser().getSession().getInvalidSessionUrl())
.permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().disable();
}
}
ok 我們來測試下
帶上token訪問用戶信息
ok 下章我們 重構短信登陸。