首页 文章

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

提问于
浏览
5

自定义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 回答

  • 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

    我有类似的要求从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

相关问题