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再去请求相应的资源。测试