這篇文章主要為大家展示了“springsecurity如何實現基于token的認證方式”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“springsecurity如何實現基于token的認證方式”這篇文章吧。
在簡析了spring security oauth的源碼之后,我們發現,其實有些源碼我們并不能用,至少,TokenEndPoint這個組件,我們就沒法用,因為這個組件只會響應/oauth/token的請求,而且spring security oauth會根據OAuth協議中常用的4種授權模式去生成令牌,而我們這里是自定義的登錄,自然用不上OAuth協議中的授權模式,因此我們改造自定義的登錄,只能借鑒其令牌生成方式。
如果有印象,在前幾篇博客中總結過自定義登錄成功處理的方式,無論前面登錄邏輯如何認證,我們只需要在認證成功之后,自定義生成AccessToken 即可,因此我們只需要重新處理我們自定義登錄成功的處理方式即可。
那么如何處理,依舊是一個問題,這就回到了上一篇博客中的內容,構造AccessToken需要OAuth3Request和Authentication,其中Authentication是登錄成功后的認證詳情信息,在登錄成功處理器中,會有相關參數傳遞進來。OAuth3Request由ClientDeatails和TokenRequest組成,這在上一篇博客中我們已經總結過了,ClientDetails根據傳遞參數中的ClientId和clientSecret等client配置信息組成,TokenRequest則由請求中其他參數實例化而成,具體如下圖所示

相關改造代碼如下
/**
* autor:liman
* createtime:2021/7/10
* comment: 自定義登錄成功處理器
*/
@Component("selfAuthenticationSuccessHandler")
@Slf4j
public class SelfAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Autowired
private SecurityProperties securityProperties;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private AuthorizationServerTokenServices authenticationServerTokenServices;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response
, Authentication authentication) throws IOException, ServletException {
log.info("自定義登錄成功的處理器");
String header = request.getHeader("Authorization");
if (header == null || !header.startsWith("Basic ")) {
throw new UnapprovedClientAuthenticationException("請求頭中沒有client相關的信息");
}
String[] tokens = extractAndDecodeHeader(header, request);
assert tokens.length == 2;
String clientId = tokens[0];
String clientSecret = tokens[1];
//得到clientDeatils信息
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);//得到clientDetails信息
if (null == clientDetails) {
throw new UnapprovedClientAuthenticationException("clientid對應的信息不存在" + clientId);
} else if (!StringUtils.equals(clientSecret, clientDetails.getClientSecret())) {
throw new UnapprovedClientAuthenticationException("clientSecret信息不匹配" + clientSecret);
}
//構建自己的tokenRequest,由于這里不能使用OAuth3中的四種授權模式,因此這里第四個參數設置為"customer"
//同理,第一個參數主要用于組裝并生成Authentication,而這里的Authentication已經通過參數傳遞進來,因此可以直接賦一個空的Map
TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP, clientId, clientDetails.getScope(), "customer");
//構建OAuth3Request
OAuth3Request oAuth3Request = tokenRequest.createOAuth3Request(clientDetails);
//構建 OAuth3Authentication
OAuth3Authentication oAuth3Authentication = new OAuth3Authentication(oAuth3Request, authentication);
//生成accessToken,這里依舊使用的是spring security oauth中默認的DefaultTokenService
OAuth3AccessToken accessToken = authenticationServerTokenServices.createAccessToken(oAuth3Authentication);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(objectMapper.writeValueAsString(accessToken));//將authentication作為json寫到前端
}
/**
* Decodes the header into a username and password.
*
* @throws BadCredentialsException if the Basic header is not present or is not valid
* Base64
*/
//TODO:解碼請求頭中的Base64編碼的 appId和AppSecret
private String[] extractAndDecodeHeader(String header, HttpServletRequest request)
throws IOException {
//格式:Basic+空格+Base64加密的appid和AppSecret,所以這里substring(6)
byte[] base64Token = header.substring(6).getBytes("UTF-8");
byte[] decoded;
try {
decoded = Base64.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)};
}
}之前提到過,由于基于token的認證交互,其實不一定會有session會話的概念,如果我們的驗證碼依舊存于session中,則并不能正常校驗,因此在基于token的短信驗證碼登錄的重構中,我們唯一要做的,就是將驗證碼存于Redis等緩存中間件中,驗證碼的key值為deviceid。
方案比較簡單,這里只貼出Redis操作驗證碼的方法
/**
* 基于redis的驗證碼存取器,避免由于沒有session導致無法存取驗證碼的問題
*/
@Component
public class RedisValidateCodeRepository implements ValidateCodeRepository {
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
/*
* (non-Javadoc)
*/
@Override
public void save(ServletWebRequest request, ValidateCode code, ValidateCodeType type) {
redisTemplate.opsForValue().set(buildKey(request, type), code, 30, TimeUnit.MINUTES);
}
/*
* (non-Javadoc)
*/
@Override
public ValidateCode get(ServletWebRequest request, ValidateCodeType type) {
Object value = redisTemplate.opsForValue().get(buildKey(request, type));
if (value == null) {
return null;
}
return (ValidateCode) value;
}
/*
* (non-Javadoc)
*
*/
@Override
public void remove(ServletWebRequest request, ValidateCodeType type) {
redisTemplate.delete(buildKey(request, type));
}
/**
* @param request
* @param type
* @return
*/
private String buildKey(ServletWebRequest request, ValidateCodeType type) {
String deviceId = request.getHeader("deviceId");
if (StringUtils.isBlank(deviceId)) {
throw new ValidateCodeException("請在請求頭中攜帶deviceId參數");
}
return "code:" + type.toString().toLowerCase() + ":" + deviceId;
}
}在調通微信社交登錄之后,再進行總結,只是需要明確的是,這里分為兩種情況,一種是簡化模式,一種是標準的OAuth3授權模式(這兩種的區別,在QQ登錄和微信登錄流程中有詳細的體現)。
簡化的OAuth模式,OAuth協議簡化的認證模式,與標準最大的不同,其實就是在獲取授權碼的時候,順帶將openId(第三方用戶id)和accessToken(獲取用戶信息的令牌),在這種前后端徹底分離的架構中,前三步前端可以通過服務提供商的SDK完成openId和AccessToken的獲取。但是并不能根據openId作為我們自己登錄系統憑證,因此我們需要提供一個根據openId進行登錄的方式這個與之前短信登錄方式大同小異

1、OpenIdAuthenticationToken
/**
* autor:liman
* createtime:2021/8/4
* comment:OpenIdAuthenticationToken
*/
public class OpenIdAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Object principal;
private String providerId;
/**
openId,和providerId作為principal
*/
public OpenIdAuthenticationToken(String openId, String providerId) {
super(null);
this.principal = openId;
this.providerId = providerId;
setAuthenticated(false);
}
/**
* This constructor should only be used by <code>AuthenticationManager</code> or
* <code>AuthenticationProvider</code> implementations that are satisfied with
* producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
* authentication token.
*
* @param principal
* @param credentials
* @param authorities
*/
public OpenIdAuthenticationToken(Object principal,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
super.setAuthenticated(true); // must use super, as we override
}
public Object getCredentials() {
return null;
}
public Object getPrincipal() {
return this.principal;
}
public String getProviderId() {
return providerId;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException(
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
}
}2、OpenIdAuthenticationFilter
/**
* autor:liman
* createtime:2021/8/4
* comment:基于openId登錄的過濾器
*/
@Slf4j
public class OpenIdAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private String openIdParameter = "openId";
private String providerIdParameter = "providerId";
private boolean postOnly = true;
public OpenIdAuthenticationFilter() {
super(new AntPathRequestMatcher("/authentication/openid", "POST"));
}
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
//獲取請求中的openId和providerId
String openid = obtainOpenId(request);
String providerId = obtainProviderId(request);
if (openid == null) {
openid = "";
}
if (providerId == null) {
providerId = "";
}
openid = openid.trim();
providerId = providerId.trim();
//構造OpenIdAuthenticationToken
OpenIdAuthenticationToken authRequest = new OpenIdAuthenticationToken(openid, providerId);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
//交給AuthenticationManager進行認證
return this.getAuthenticationManager().authenticate(authRequest);
}
/**
* 獲取openId
*/
protected String obtainOpenId(HttpServletRequest request) {
return request.getParameter(openIdParameter);
}
/**
* 獲取提供商id
*/
protected String obtainProviderId(HttpServletRequest request) {
return request.getParameter(providerIdParameter);
}
protected void setDetails(HttpServletRequest request, OpenIdAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
public void setOpenIdParameter(String openIdParameter) {
Assert.hasText(openIdParameter, "Username parameter must not be empty or null");
this.openIdParameter = openIdParameter;
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public final String getOpenIdParameter() {
return openIdParameter;
}
public String getProviderIdParameter() {
return providerIdParameter;
}
public void setProviderIdParameter(String providerIdParameter) {
this.providerIdParameter = providerIdParameter;
}
}3、OpenIdAuthenticationProvider
/**
*
*/
package com.learn.springsecurity.app.social.openid;
/**
* @author zhailiang
*
*/
public class OpenIdAuthenticationProvider implements AuthenticationProvider {
private SocialUserDetailsService userDetailsService;
private UsersConnectionRepository usersConnectionRepository;
/*
* (non-Javadoc)
*
* @see org.springframework.security.authentication.AuthenticationProvider#
* authenticate(org.springframework.security.core.Authentication)
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
OpenIdAuthenticationToken authenticationToken = (OpenIdAuthenticationToken) authentication;
Set<String> providerUserIds = new HashSet<>();
providerUserIds.add((String) authenticationToken.getPrincipal());
//之前社交登錄中介紹的usersConnectionRepository,從user_connection表中根據providerId和openId查詢用戶id
Set<String> userIds = usersConnectionRepository.findUserIdsConnectedTo(authenticationToken.getProviderId(), providerUserIds);
if(CollectionUtils.isEmpty(userIds) || userIds.size() != 1) {
throw new InternalAuthenticationServiceException("無法獲取用戶信息");
}
//獲取到userId了
String userId = userIds.iterator().next();
//利用UserDetailsService根據userId查詢用戶信息
UserDetails user = userDetailsService.loadUserByUserId(userId);
if (user == null) {
throw new InternalAuthenticationServiceException("無法獲取用戶信息");
}
OpenIdAuthenticationToken authenticationResult = new OpenIdAuthenticationToken(user, user.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
/*
* (non-Javadoc)
*
* @see org.springframework.security.authentication.AuthenticationProvider#
* supports(java.lang.Class)
*/
@Override
public boolean supports(Class<?> authentication) {
return OpenIdAuthenticationToken.class.isAssignableFrom(authentication);
}
public SocialUserDetailsService getUserDetailsService() {
return userDetailsService;
}
public void setUserDetailsService(SocialUserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public UsersConnectionRepository getUsersConnectionRepository() {
return usersConnectionRepository;
}
public void setUsersConnectionRepository(UsersConnectionRepository usersConnectionRepository) {
this.usersConnectionRepository = usersConnectionRepository;
}
}4、配置類
/**
* @author zhailiang
*
*/
@Component
public class OpenIdAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private AuthenticationSuccessHandler selfAuthenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler selfAuthenticationFailureHandler;
@Autowired
private SocialUserDetailsService userDetailsService;
@Autowired
private UsersConnectionRepository usersConnectionRepository;
@Override
public void configure(HttpSecurity http) throws Exception {
OpenIdAuthenticationFilter OpenIdAuthenticationFilter = new OpenIdAuthenticationFilter();
OpenIdAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
OpenIdAuthenticationFilter.setAuthenticationSuccessHandler(selfAuthenticationSuccessHandler);
OpenIdAuthenticationFilter.setAuthenticationFailureHandler(selfAuthenticationFailureHandler);
OpenIdAuthenticationProvider OpenIdAuthenticationProvider = new OpenIdAuthenticationProvider();
OpenIdAuthenticationProvider.setUserDetailsService(userDetailsService);
OpenIdAuthenticationProvider.setUsersConnectionRepository(usersConnectionRepository);
http.authenticationProvider(OpenIdAuthenticationProvider)
.addFilterAfter(OpenIdAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}測試結果

標準的OAuth模式

針對標準的授權模式,我們并不需要做多少改動,因為在社交登錄那一節中我們已經做了相關開發,只是需要說明的是,只是在spring-social的過濾器——SocialAuthenticationFilter中,在正常社交登錄流程完成之后會默認跳轉到某個頁面,而這個并不適用于前后端分離的項目,因此要針對這個問題定制化解決。這需要回到之前SocialAuthenticationFilter加入到認證過濾器鏈上的代碼。之前我們說過社交登錄的過濾器鏈不需要我們手動配置,只需要初始化SpringSocialConfiguer的時候,會自動加入到社交登錄的認證過濾器鏈上
@Configuration
@EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {
@Bean
public SpringSocialConfigurer selfSocialSecurityConfig(){
SpringSocialConfigurer selfSpringSocialConfig = new SpringSocialConfigurer();
return selfSpringSocialConfig;
}
}我們只需要改變SocialAuthenticationFilter的默認處理即可,因此我們給他加一個后置處理器,但是這個后置處理器是在SpringSocialConfigurer的postProcess函數中進行處理
/**
* autor:liman
* createtime:2021/7/15
* comment:自定義的springsocial配置類
*/
public class SelfSpringSocialConfig extends SpringSocialConfigurer {
private String processFilterUrl;
@Autowired(required = false)
private ConnectionSignUp connectionSignUp;
@Autowired(required = false)
private SocialAuthenticationFilterPostProcessor socialAuthenticationFilterPostProcessor;
public SelfSpringSocialConfig(String processFilterUrl) {
this.processFilterUrl = processFilterUrl;
}
@Override
protected <T> T postProcess(T object) {
SocialAuthenticationFilter socialAuthenticationFilter = (SocialAuthenticationFilter) super.postProcess(object);
socialAuthenticationFilter.setFilterProcessesUrl(processFilterUrl);
if(null!=socialAuthenticationFilterPostProcessor){
socialAuthenticationFilterPostProcessor.process(socialAuthenticationFilter);
}
return (T) socialAuthenticationFilter;
}
public ConnectionSignUp getConnectionSignUp() {
return connectionSignUp;
}
public void setConnectionSignUp(ConnectionSignUp connectionSignUp) {
this.connectionSignUp = connectionSignUp;
}
public SocialAuthenticationFilterPostProcessor getSocialAuthenticationFilterPostProcessor() {
return socialAuthenticationFilterPostProcessor;
}
public void setSocialAuthenticationFilterPostProcessor(SocialAuthenticationFilterPostProcessor socialAuthenticationFilterPostProcessor) {
this.socialAuthenticationFilterPostProcessor = socialAuthenticationFilterPostProcessor;
}
}
//將我們自定義的 SpringSocialConfigurer交給spring托管
@Configuration
@EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {
@Bean
public SpringSocialConfigurer selfSocialSecurityConfig(){
String processFilterUrl = securityProperties.getSocial().getProcessFilterUrl();
SelfSpringSocialConfig selfSpringSocialConfig = new SelfSpringSocialConfig(processFilterUrl);
//指定第三方用戶信息認證不存在的注冊頁
selfSpringSocialConfig.signupUrl(securityProperties.getBrowser().getSiguUpPage());
selfSpringSocialConfig.setConnectionSignUp(connectionSignUp);
selfSpringSocialConfig.setSocialAuthenticationFilterPostProcessor(socialAuthenticationFilterPostProcessor);
return selfSpringSocialConfig;
}
}我們自定義的過濾器后置處理器如下
/**
* autor:liman
* createtime:2021/8/7
* comment:APP社交登錄認證后置處理器
*/
@Component
public class AppSocialAuthenticationFilterPostProcessor implements SocialAuthenticationFilterPostProcessor {
@Autowired
private AuthenticationSuccessHandler selfAuthenticationSuccessHandler;
@Override
public void process(SocialAuthenticationFilter socialAuthenticationFilter) {
socialAuthenticationFilter.setAuthenticationSuccessHandler(selfAuthenticationSuccessHandler);
}
}這里需要總結一下之前的社交登錄中用戶注冊綁定的操作。
在之前的社交登錄中,如果spring social發現用戶是第一次登錄,則會跳轉到相關的頁面,這個頁面我們其實也可以自己定義并配置
@Configuration
@EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {
@Bean
public SpringSocialConfigurer selfSocialSecurityConfig(){
String processFilterUrl = securityProperties.getSocial().getProcessFilterUrl();
SelfSpringSocialConfig selfSpringSocialConfig = new SelfSpringSocialConfig(processFilterUrl);
//指定第三方用戶信息認證不存在的注冊頁
selfSpringSocialConfig.signupUrl(securityProperties.getBrowser().getSiguUpPage());
selfSpringSocialConfig.setConnectionSignUp(connectionSignUp);
selfSpringSocialConfig.setSocialAuthenticationFilterPostProcessor(socialAuthenticationFilterPostProcessor);
return selfSpringSocialConfig;
}
@Bean
public ProviderSignInUtils providerSignInUtils(ConnectionFactoryLocator connectionFactoryLocator){
return new ProviderSignInUtils(connectionFactoryLocator,
getUsersConnectionRepository(connectionFactoryLocator));
}
}我們配置的代碼中,可以自定義頁面路徑,我們自定義頁面如下(一個簡單的登錄綁定頁面)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>登錄</title> </head> <body> <h3>Demo注冊頁</h3> <form action="user/regist" method="post"> <table> <tr> <td>用戶名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密碼:</td> <td><input type="password" name="password"></td> </tr> <tr> <td colspan="2"> <button type="submit" name="type" value="regist">注冊</button> <button type="submit" name="type" value="binding">綁定</button> </td> </tr> </table> </form> </body> </html>
在用戶第一次跳轉到這個頁面的用戶選擇注冊,或者綁定,都會請求/user/register接口,這個接口借助providerSignInUtils完成會話中的用戶數據更新
@Autowired
private ProviderSignInUtils providerSignInUtils;
@PostMapping("/register")
public void userRegister(@RequestBody User user, HttpServletRequest request) {
//利用providerSignInUtils,將注冊之后的用戶信息,關聯到會話中
providerSignInUtils.doPostSignUp(user.getId(),new ServletWebRequest(request));
}在跳轉之前,spring social已經幫我們將用戶信息存入會話(在SocialAuthenticationFilter中可以看到相關代碼)
//以下代碼位于:org.springframework.social.security.SocialAuthenticationFilter#doAuthentication
private Authentication doAuthentication(SocialAuthenticationService<?> authService, HttpServletRequest request, SocialAuthenticationToken token) {
try {
if (!authService.getConnectionCardinality().isAuthenticatePossible()) return null;
token.setDetails(authenticationDetailsSource.buildDetails(request));
Authentication success = getAuthenticationManager().authenticate(token);
Assert.isInstanceOf(SocialUserDetails.class, success.getPrincipal(), "unexpected principle type");
updateConnections(authService, token, success);
return success;
} catch (BadCredentialsException e) {
// connection unknown, register new user?
if (signupUrl != null) {
//這里就是將社交用戶信息存入會話
// store ConnectionData in session and redirect to register page
sessionStrategy.setAttribute(new ServletWebRequest(request), ProviderSignInAttempt.SESSION_ATTRIBUTE, new ProviderSignInAttempt(token.getConnection()));
throw new SocialAuthenticationRedirectException(buildSignupUrl(request));
}
throw e;
}
}但是基于前后端分離,且并沒有會話對象交互的系統,這種方式并不適用,因為并不存在會話,如何處理,需要用其他方案,其實我們可以在驗證碼登錄的改造中受到啟發,將用戶數據存入會話即可,我們自定義實現一個providerSignInUtils將用戶信息存入Redis即可。
1、將第三方用戶數據存入Redis的工具類
/**
* autor:liman
* createtime:2021/8/7
* comment:app端用戶信息存入Redis的工具類
*/
@Component
public class AppSignUpUtils {
public static final String SOCIAL_REDIS_USER_PREFIX = "self:security:social:connectionData";
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
@Autowired
private UsersConnectionRepository usersConnectionRepository;
@Autowired
private ConnectionFactoryLocator connectionFactoryLocator;
public void saveConnectionData(WebRequest webRequest, ConnectionData connectionData) {
redisTemplate.opsForValue().set(getKey(webRequest), connectionData, 10, TimeUnit.MINUTES);
}
/**
* 將用戶與數據庫中的信息進行綁定
* @param request
* @param userId
*/
public void doPostSignUp(WebRequest request,String userId){
String key = getKey(request);
if(!redisTemplate.hasKey(key)){
throw new RuntimeException("無法找到緩存的用戶社交賬號信息");
}
ConnectionData connectionData = (ConnectionData) redisTemplate.opsForValue().get(key);
//根據ConnectionData實例化創建一個Connection
Connection<?> connection = connectionFactoryLocator.getConnectionFactory(connectionData.getProviderId())
.createConnection(connectionData);
//將數據庫中的用戶與Redis中的用戶信息關聯
usersConnectionRepository.createConnectionRepository(userId).addConnection(connection);
}
/**
* 獲取設備id作為key
*
* @param webRequest
* @return
*/
public String getKey(WebRequest webRequest) {
String deviceId = webRequest.getHeader("deviceId");
if (StringUtils.isBlank(deviceId)) {
throw new RuntimeException("設備id不能為空");
}
return SOCIAL_REDIS_USER_PREFIX + deviceId;
}
}2、復寫掉原來的配置類
為了避免對原有代碼的侵入性處理,這里我們需要自定義一個實現BeanPostProcessor接口的類
/**
* autor:liman
* createtime:2021/8/7
* comment:由于app端的社交用戶綁定,不能采用跳轉,也不能操作會話,需要用自定義的providerSignUpUtils工具類
* 因此需要定義一個后置處理器,針對SpringSocialConfigurer進行一些后置處理
*/
@Component
public class AppSpringSocialConfigurerPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return null;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(StringUtils.equals(beanName,"selfSocialSecurityConfig")){
SelfSpringSocialConfig configurer = (SelfSpringSocialConfig) bean;
//復寫掉原有的SelfSpringSocialConfig的signupUrl
configurer.signupUrl("/app/social/signup");
return configurer;
}
return bean;
}
}針對上述的請求路徑,我們也要寫一個對應路徑的controller處理方法
@RestController
@Slf4j
public class AppSecurityController {
@Autowired
private ProviderSignInUtils providerSignInUtils;
@Autowired
private AppSignUpUtils appSignUpUtils;
@GetMapping("/app/social/signup")
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public BaseResponse getSocialUserInfo(HttpServletRequest request){
BaseResponse result = new BaseResponse(StatusCode.Success);
log.info("【app模式】開始獲取會話中的第三方用戶信息");
//先從其中拿出數據,畢竟這個時候還沒有完全跳轉,下一個會話,就沒有該數據了
Connection<?> connectionFromSession = providerSignInUtils.getConnectionFromSession(new ServletWebRequest(request));
SocialUserInfo socialUserInfo = new SocialUserInfo();
socialUserInfo.setProviderId(connectionFromSession.getKey().getProviderId());
socialUserInfo.setProviderUserId(connectionFromSession.getKey().getProviderUserId());
socialUserInfo.setNickName(connectionFromSession.getDisplayName());
socialUserInfo.setHeadImg(connectionFromSession.getImageUrl());
//轉存到自己的工具類中
appSignUpUtils.saveConnectionData(new ServletWebRequest(request),connectionFromSession.createData());
result.setData(socialUserInfo);
return result;
}
}對于用戶注冊的接口也需要做調整
@PostMapping("/register")
public void userRegister(@RequestBody User user, HttpServletRequest request) {
//如果是瀏覽器的應用利用providerSignInUtils,將注冊之后的用戶信息,關聯到會話中
providerSignInUtils.doPostSignUp(user.getId(),new ServletWebRequest(request));
//如果是app的應用,則利用appSignUpUtils 將注冊之后的用戶信息,關聯到會話中
appSignUpUtils.doPostSignUp(new ServletWebRequest(request),user.getId());
}以上是“springsecurity如何實現基于token的認證方式”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。