3.那麼在哪裏去運用上面AuthorizationServerTokenServices呢?是在咱們的:AuthenticationSuccessHandler裏面。以下圖: 前端
4.咱們以前在spring-security-web項目裏面寫過AuthenticationSuccessHandler實現類的處理邏輯:若是是json登陸,咱們吧登陸成功信息authentication直接以json形式返回web
@Component("myAuthenticationSuccessHandler") public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ObjectMapper objectMapper; @Autowired private SecurityProperties securityProperties; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { /** * 將Authentication轉換成json返回給前端 * 參數:authentication 使用不一樣登陸方式,其值是不同的,這是一個接口在實際運轉中,他會傳不一樣的實現對象過來 */ logger.info("登陸成功"); if(LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){//JSON異步登陸 response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(authentication)); }else { //非json就是使用父類處理器---父類處理器就是跳轉 super.onAuthenticationSuccess(request,response,authentication); } } }
在app端時候,咱們在登陸認證成功以後,咱們返回的再也不是用戶的認證信息,而是:用TokenServices生成的OAuth2AccessToken;從上一節咱們知道,咱們若是要生成OAuth2AccessToken就須要OAuth2Authentication;生成OAuth2Authentication咱們又須要OAuth2Request和Authentication。以前在Spring Security OAuth中是根據TokenGranter的不一樣實現模式(根據傳遞的grant_type)來構建生成OAuth2Authentication咱們又須要OAuth2Request和Authentication。可是在咱們登陸成功處理器裏面的方法參數上就有一個:Authentication authentication;因此在咱們的代碼邏輯裏面是不須要建立Authentication的業務邏輯的(因其在登陸時候就已經建立好了),咱們只須要建立處理OAuth2Request。spring
前言總結三點:json
咱們參考Spring Security OAuth2咱們知道,咱們使用BasicAuthenticationFilter解析 安全
MyAuthenticationSuccessHandler代碼:服務器
@Component("myAuthenticationSuccessHandler") public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ObjectMapper objectMapper; @Autowired private ClientDetailsService clientDetailsService; @Autowired private AuthorizationServerTokenServices authorizationServerTokenServices; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { /** * 將Authentication轉換成json返回給前端 * 參數:authentication 使用不一樣登陸方式,其值是不同的,這是一個接口在實際運轉中,他會傳不一樣的實現對象過來 */ logger.info("登陸成功"); //1.解析clientId String header = request.getHeader("Authorization"); if (header == null && !header.startsWith("Basic ")) { throw new UnapprovedClientAuthenticationException("請求頭中無clientId信息"); } String[] tokens = extractAndDecodeHeader(header, request); assert tokens.length == 2; String clientId = tokens[0]; String clientSecret = tokens[1]; //2.獲取ClientDetails並做校驗 ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId); if(null == clientDetails){//說明咱們根據配置的yxm拿不到第三方ClientDetails信息。咱們應該拋出異常 throw new UnapprovedClientAuthenticationException("clientId對應的配置信息不存在:"+clientId); }else if(!StringUtils.equals(clientDetails.getClientSecret(),clientSecret)){//若是clientDetails存在,咱們就應該校驗clientSeret throw new UnapprovedClientAuthenticationException("clientSecret不匹配:"+clientSecret); } //3.獲取TokenRequest:第一個參數map主要是爲了建立Authentication;可是上面Authentication已經建立。咱們能夠將其設置爲空 //由於grantType爲:受權類型 以前是:password、authentication_code、implit、Client Credential、咱們這裏爲自定義模式 /** * 類:ResourceOwnerPasswordTokenGranter * * protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { * Map<String, String> parameters = new LinkedHashMap(tokenRequest.getRequestParameters()); * String username = (String)parameters.get("username"); * String password = (String)parameters.get("password"); * parameters.remove("password"); * Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password); * } */ TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP,clientId, clientDetails.getScope(), "custom"); //4.建立OAuth2Request OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails); //5.建立OAuth2Authentication OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); OAuth2AccessToken accessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication); //6.響應返回 response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(accessToken)); } private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException { byte[] base64Token = header.substring(6).getBytes("UTF-8"); byte[] decoded; try { decoded = Base64.decode(base64Token); } catch (IllegalArgumentException var7) { 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"); } else { return new String[]{token.substring(0, delim), token.substring(delim + 1)}; } } }
有了上面其實仍是不夠的,資源服務器目前只是用了Spring Security Oauth2的默認配置。以前咱們寫spring-security-web項目的時候的安全配置:WebSecurityConfig主配置類中,全部的配置都是在configure裏面的。如今同理咱們也須要一個配置。也就是咱們註解:@EnableResourceServer修飾的類。app
@Configuration @EnableResourceServer public class MyResourceServerConfig extends ResourceServerConfigurerAdapter { @Autowired private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Autowired private MyAuthenticationFailureHandler myAuthenticationFailureHandler; @Autowired private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig; //短信驗證碼受權配置 @Autowired private SpringSocialConfigurer mySocialSecurityConfig; @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) .successHandler(myAuthenticationSuccessHandler) .failureHandler(myAuthenticationFailureHandler); http.apply(smsCodeAuthenticationSecurityConfig) .and() .apply(mySocialSecurityConfig)//配置第三方social .and() .authorizeRequests() .antMatchers( SecurityConstants.DEFAULT_UNAUTHENTICATION_URL, SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, securityProperties.getBrowser().getLoginPage(), SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*", securityProperties.getBrowser().getSignUpUrl()) .permitAll() .anyRequest() .authenticated() .and() .csrf().disable(); } }
在spring-security-demo項目咱們以前是訪問:"/authentication/form"來訪問登陸的。咱們專門寫了頁面,如今咱們使用app端登陸,咱們使用:restlet_client提交請求。咱們書寫用戶名和密碼。
組織請求參數: 異步
咱們攜帶這個access_token去訪問用戶信息:ide
{ "access_token": "4f04305c-a956-4546-95ba-20ff8d9ed2e3", "token_type": "bearer", "refresh_token": "76abf98d-dbe5-4f2e-8f18-cf83c6f3a2c5", "expires_in": 43199 }
其實咱們如今已經達到咱們的要求了。app去調用咱們自個表單登陸的服務請求。而後給他返回一個OAuth2AccessToken。而後他拿着這個Access_Token再去請求相應的資源。測試