使用Spring社交集成定制Spring Security OAuth2

时间:2016-12-18 00:23:40

标签: spring spring-security oauth-2.0 spring-security-oauth2

自定义Spring安全性OAuth2工作正常,现在想添加Spring Social集成(facebook登录,google登录等),当用户点击Facebook登录(用户不提供任何用户名/密码)时,Facebook将返回access_token,但是这个access_token我们不能用来查询我的应用程序web服务,为了得到我的应用程序access_token,我们需要传递用户名和密码,并使用grant_type作为密码。以下是我的配置文件

AuthorizationServerConfiguration.java

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    DataSource dataSource;

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(
            AuthorizationServerSecurityConfigurer oauthServer) 
                    throws Exception {
        oauthServer.allowFormAuthenticationForClients();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) 
            throws Exception {
        clients.jdbc(dataSource);
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setTokenStore(tokenStore());
        tokenServices.setAccessTokenValiditySeconds(86400000);
        tokenServices.setRefreshTokenValiditySeconds(86400000);
        return tokenServices;
    }


    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) 
            throws Exception {
        endpoints
        .tokenServices(tokenServices())
        .authenticationManager(authenticationManager);
    }

    @Bean
    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }
}

ResourceServerConfiguration.java

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    private String resourceId = "rest_api";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        // @formatter:off
        resources.resourceId(resourceId);
        // @formatter:on
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
        .antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
        .antMatchers(HttpMethod.GET, "/**/login").permitAll()
        .antMatchers(HttpMethod.GET, "/**/callback").permitAll()
        .anyRequest().authenticated()
        .and()
        .formLogin().permitAll();
    }
}

最后 WebSecurityConfigurerAdapter.java

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) 
      throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() 
      throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
         http.csrf().disable()
         .authorizeRequests()
         .antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
         .antMatchers(HttpMethod.GET, "/**/login").permitAll()
         .antMatchers(HttpMethod.GET, "/**/callback").permitAll()
         .anyRequest().authenticated()
         .and()
         .formLogin().permitAll();
    }
}

已经在SO中阅读了不同的帖子,但无法获得任何有效的示例,请指导我。在此先感谢。!

2 个答案:

答案 0 :(得分:1)

    String redirectURL = messages.getProperty(Constant.REDIRECT_URI.getValue());
    String clientSecret = messages.getProperty(Constant.CLIENT_SECRET.getValue());
    HttpHeaders header = new HttpHeaders();
    header.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED);

    String req = "client_id=myas&" + "client_secret=" + clientSecret + "&grant_type=authorization_code&"
            + "scope=user_profile&" + "code=" + loginReqeust.getCode() + "&redirect_uri="
            + loginReqeust.getRedirectURL();

    HttpEntity<String> body = new HttpEntity<String>(req, header);
    Map<Object, Object> mapRes = new LinkedHashMap<Object, Object>();

    // call to get access token
    mapRes = getEndpoint("https://auth.mygov.in/oauth2/token", null, body, null);
    String accessToken = mapRes.get("access_token").toString();


    // Call for getting User Profile

    String userUrl = "https://auth.mygov.in/myasoauth2/user/profile";

    HttpHeaders head = new HttpHeaders();
    head.add("Authorization", "Bearer " + accessToken);

    HttpEntity<String> ent = new HttpEntity<String>(head);
    Map<Object, Object> mapResponse = new LinkedHashMap<Object, Object>();
    mapResponse.put("userProfile", getEndpoint(userUrl, null, ent, null));

    //In my case userKey represents the username basically the email of the user using which he/she logged into facebook/google

    String userKey = (String) ((LinkedHashMap<Object, Object>) mapResponse.get("userProfile")).get("mail");

    // Store the user profile in your database with basic info like username & an autogenerated password for the time being and other basic fields.

    userService.save(userprofileInfo);                  

                mapResponse.put("username", "retrieved from facebook/google user's profile");
                mapResponse.put("password", "autogenerated by your application");   

    //send back this response (mapResponse) to your UI and then from there make a call by passing this username and pwd to retrieve the access_token from your own applicatioon.

答案 1 :(得分:1)

我有类似的要求从facebook获取访问令牌并通过验证服务器端的facebook令牌生成自己的JWT令牌。

我修改了这里提到的项目: https://github.com/svlada/springboot-security-jwt

我的自定义如下(我假设您已经拥有了facebook访问令牌):

<强> LoginRequest.java

public class LoginRequest {
private String token;

@JsonCreator
public LoginRequest(@JsonProperty("token") String token) {
    this.token = token;
}

public String getToken() {
    return token;
}

public void setToken(String token) {
    this.token = token;
}
}

<强> AjaxLoginProcessingFilter.java

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException, IOException, ServletException {
    if (!HttpMethod.POST.name().equals(request.getMethod()) || !WebUtil.isAjax(request)) {
        if(logger.isDebugEnabled()) {
            logger.debug("Authentication method not supported. Request method: " + request.getMethod());
        }
        throw new AuthMethodNotSupportedException("Authentication method not supported");
    }

    LoginRequest loginRequest = objectMapper.readValue(request.getReader(), LoginRequest.class);

    if (StringUtils.isBlank(loginRequest.getToken())) {
        throw new AuthenticationServiceException("token not provided");
    }

    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginRequest.getToken(), null);

    return this.getAuthenticationManager().authenticate(token);
}

<强> AjaxAuthenticationProvider.java

@Component
public class AjaxAuthenticationProvider implements AuthenticationProvider {
@Autowired private BCryptPasswordEncoder encoder;
@Autowired private DatabaseUserService userService;


@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    Assert.notNull(authentication, "No authentication data provided");
    String username = null;
    try {
        username = getUsername(authentication.getPrincipal());
    } catch (UnsupportedOperationException e) {

    } catch (IOException e) {

    }

    //You can either register this user by fetching additional data from facebook or reject it.        
    User user = userService.getByUsername(username).orElseThrow(() -> new UsernameNotFoundException("User not found"));

    if (user.getRoles() == null) throw new InsufficientAuthenticationException("User has no roles assigned");

    List<GrantedAuthority> authorities = user.getRoles().stream()
            .map(authority -> new SimpleGrantedAuthority(authority.getRole().authority()))
            .collect(Collectors.toList());

    UserContext userContext = UserContext.create(user.getUsername(), authorities);

    return new UsernamePasswordAuthenticationToken(userContext, null, userContext.getAuthorities());
}

private String getUsername(Object principal) throws UnsupportedOperationException, IOException {
    HttpClient client = new DefaultHttpClient();
    //I am just accessing the details. You can debug whether this token was granted against your app.
    HttpGet get = new HttpGet("https://graph.facebook.com/me?access_token=" + principal.toString());
    HttpResponse response = client.execute(get);

    BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));

    StringBuffer result = new StringBuffer();
    String line = "";
    while ((line = rd.readLine()) != null) {
        result.append(line);
    }

    JSONObject o = new JSONObject(result.toString());

    //This is just for demo. You should use id or some other unique field.
    String username = o.getString("first_name");
    return username;
}

@Override
public boolean supports(Class<?> authentication) {
    return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}

除此之外,我还必须添加自定义BeanPostProcessor来覆盖UsernamePasswordAuthenticationFilter的默认行为,以仅接受令牌作为字段而不是用户名和密码。

<强> UserPassAuthFilterBeanPostProcessor.java

公共类UserPassAuthFilterBeanPostProcessor实现BeanPostProcessor {

private String usernameParameter;
private String passwordParameter;

@Override
public final Object postProcessAfterInitialization(final Object bean,
    final String beanName) {
    return bean;
}

@Override
public final Object postProcessBeforeInitialization(final Object bean,
    final String beanName) {
    if (bean instanceof UsernamePasswordAuthenticationFilter) {
        final UsernamePasswordAuthenticationFilter filter =
            (UsernamePasswordAuthenticationFilter) bean;
        filter.setUsernameParameter(getUsernameParameter());
        filter.setPasswordParameter(getPasswordParameter());
    }

    return bean;
}

public final void setUsernameParameter(final String usernameParameter) {
    this.usernameParameter = usernameParameter;
}

public final String getUsernameParameter() {
    return usernameParameter;
}

public final void setPasswordParameter(final String passwordParameter) {
    this.passwordParameter = passwordParameter;
}

public final String getPasswordParameter() {
    return passwordParameter;
}

配置:

@Bean
public UserPassAuthFilterBeanPostProcessor  userPassAuthFilterBeanPostProcessor(){
    UserPassAuthFilterBeanPostProcessor bean = new UserPassAuthFilterBeanPostProcessor();
    bean.setUsernameParameter("token");
    bean.setPasswordParameter(null);
    return bean;
}

postman output