我已经设置了一个小项目,使用 Spring Boot (1.5.2)、Spring Security 和 Spring Security OAuth2 实现 OAuth2 Login 和 Google+ API。
您可以在以下位置找到来源:https://github.com/ccoloradoc/OAuth2Sample
我能够通过 google 进行身份验证并提取用户信息。但是,在我尝试将“https://accounts.google.com/o/oauth2/auth”与我的 RestTemplate 连接以调用 google api 之后,我无法再次登录,因为我收到了“400 Bad Request”。
请参阅过滤器尝试身份验证方法以获取更多引用。
这是我的安全配置类
@Configuration
@EnableGlobalAuthentication
@EnableOAuth2Client
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@PropertySource(value = {"classpath:oauth.properties"})
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Resource
@Qualifier("accessTokenRequest")
private AccessTokenRequest accessTokenRequest;
@Autowired
private OAuth2ClientContextFilter oAuth2ClientContextFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.
authorizeRequests()
.antMatchers(HttpMethod.GET, "/login","/public/**", "/resources/**","/resources/public/**").permitAll()
.antMatchers("/google_oauth2_login").anonymous()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/")
.and()
.csrf().disable()
.logout()
.logoutSuccessUrl("/")
.logoutUrl("/logout")
.deleteCookies("remember-me")
.and()
.rememberMe()
.and()
.addFilterAfter(oAuth2ClientContextFilter,ExceptionTranslationFilter.class)
.addFilterAfter(googleOAuth2Filter(),OAuth2ClientContextFilter.class)
.userDetailsService(userDetailsService);
// @formatter:on
}
@Bean
@ConfigurationProperties("google.client")
public OAuth2ProtectedResourceDetails auth2ProtectedResourceDetails() {
return new AuthorizationCodeResourceDetails();
}
@Bean
public OAuth2RestTemplate oauth2RestTemplate() {
return new OAuth2RestTemplate(auth2ProtectedResourceDetails(),
new DefaultOAuth2ClientContext(accessTokenRequest));
}
@Bean
public GoogleOAuth2Filter googleOAuth2Filter() {
return new GoogleOAuth2Filter("/google_oauth2_login");
}
/*
* Building our custom Google Provider
* */
@Bean
public GoogleOauth2AuthProvider googleOauth2AuthProvider() {
return new GoogleOauth2AuthProvider();
}
/*
* Using autowired to assign it to the auth manager
* */
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(googleOauth2AuthProvider());
}
@Bean
public SpringSecurityDialect springSecurityDialect() {
return new SpringSecurityDialect();
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
这是我的身份验证提供程序:
public class GoogleOauth2AuthProvider implements AuthenticationProvider {
private static final Logger logger = LoggerFactory.getLogger(GoogleOauth2AuthProvider.class);
@Autowired(required = true)
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
logger.info("Provider Manager Executed");
CustomOAuth2AuthenticationToken token = (CustomOAuth2AuthenticationToken) authentication;
UserDetailsImpl registeredUser = (UserDetailsImpl) token.getPrincipal();
try {
registeredUser = (UserDetailsImpl) userDetailsService
.loadUserByUsername(registeredUser.getEmail());
} catch (UsernameNotFoundException usernameNotFoundException) {
logger.info("User trying google/login not already a registered user. Register Him !!");
}
return token;
}
@Override
public boolean supports(Class<?> authentication) {
return CustomOAuth2AuthenticationToken.class
.isAssignableFrom(authentication);
}
}
UserDetailService 是 Spring Security Core 的一个实现,它从数据库中读取用户并将其转换为实现 Spring Security Core UserDetails 的 UserDetails POJO。
这是我的过滤器实现:
public class GoogleOAuth2Filter extends AbstractAuthenticationProcessingFilter {
/**
* Logger
*/
private static final Logger log = LoggerFactory.getLogger(GoogleOAuth2Filter.class);
private static final Authentication dummyAuthentication;
static {
dummyAuthentication = new UsernamePasswordAuthenticationToken(
"dummyUserName23452346789", "dummyPassword54245",
CustomUserDetails.DEFAULT_ROLES);
}
private static final String NAME = "name";
private static final String EMAIL = "email";
private static final String PICTURE = "picture";
private static final Logger logger = LoggerFactory
.getLogger(GoogleOAuth2Filter.class);
@Value(value = "${google.authorization.url}")
private String googleAuhorizationUrl;
public GoogleOAuth2Filter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
}
@Autowired
private UserService userService;
@Autowired
private OAuth2RestTemplate oauth2RestTemplate;
@Autowired
@Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException,
IOException, ServletException {
logger.info("Google Oauth Filter Triggered!!");
URI authURI;
try {
authURI = new URI(googleAuhorizationUrl);
} catch (URISyntaxException e) {
log.error("\n\n\n\nERROR WHILE CREATING GOOGLE AUTH URL", e);
return null;
}
SecurityContext context = SecurityContextHolder.getContext();
// auth null or not authenticated.
String code = request.getParameter("code");
Map<String, String[]> parameterMap = request.getParameterMap();
logger.debug(parameterMap.toString());
if (StringUtils.isEmpty(code)) {
// Google authentication in progress. will return null.
logger.debug("Will set dummy user in context ");
context.setAuthentication(dummyAuthentication);
// trigger google oauth2.
// ERROR ON SECOND LOGIN ATTEMPT
oauth2RestTemplate.postForEntity(authURI, null, Object.class);
return null;
} else {
logger.debug("Response from Google Recieved !!");
ResponseEntity<Object> forEntity = oauth2RestTemplate.getForEntity(
"https://www.googleapis.com/plus/v1/people/me/openIdConnect",
Object.class);
@SuppressWarnings("unchecked")
Map<String, String> profile = (Map<String, String>) forEntity.getBody();
CustomOAuth2AuthenticationToken authenticationToken = getOAuth2Token(
profile.get(EMAIL), profile.get(NAME), profile.get(PICTURE));
authenticationToken.setAuthenticated(false);
return getAuthenticationManager().authenticate(authenticationToken);
}
}
private CustomOAuth2AuthenticationToken getOAuth2Token(
String email, String name, String picture) {
User user = userService.findByEmail(email);
//Register user
if(user == null) {
user = new User(name, email, picture);
userService.saveOrUpdate(user);
}
UserDetailsImpl registeredUser = new UserDetailsImpl(name, email, picture);
CustomOAuth2AuthenticationToken authenticationToken =
new CustomOAuth2AuthenticationToken(registeredUser);
return authenticationToken;
}
}
最佳答案
感谢 Cristian,您不知道您的代码对我自己的代码奠定了基础。我修改了你原来的OAuth2 Github项目,改成如下代码。
GoogleOAuth2Filter.java
package tech.aabo.celulascontentas.oauth.filter;
import static java.lang.Math.toIntExact;
import com.google.api.client.auth.oauth2.AuthorizationCodeResponseUrl;
import com.google.api.client.auth.oauth2.TokenResponse;
import com.google.api.client.auth.oauth2.TokenResponseException;
import com.google.api.client.googleapis.auth.oauth2.*;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.plus.Plus;
import com.google.api.services.plus.model.Person;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.configurationprocessor.json.JSONException;
import org.springframework.boot.configurationprocessor.json.JSONObject;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import tech.aabo.celulascontentas.oauth.domain.User;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.Arrays;
import java.util.Calendar;
import java.util.UUID;
/**
* Created by colorado on 9/03/17.
* Modified by frhec on 7/06/18
*/
public class GoogleOAuth2Filter extends AbstractAuthenticationProcessingFilter {
/**
* Logger
*/
private static final Logger logger = LoggerFactory.getLogger(GoogleOAuth2Filter.class);
public GoogleOAuth2Filter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
}
@Autowired
@Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String CLIENT_SECRET_FILE = "client_secret.json";
SecurityContext context = SecurityContextHolder.getContext();
if(context.getAuthentication() == null) {
GoogleClientSecrets clientSecrets = loadSecret(CLIENT_SECRET_FILE);
if (StringUtils.isEmpty(request.getQueryString())) {
try {
GoogleAuthorizationCodeRequestUrl auth = new GoogleAuthorizationCodeRequestUrl(clientSecrets.getDetails().getClientId(),
request.getRequestURL().toString(), Arrays.asList(
"https://www.googleapis.com/auth/plus.login",
"https://www.googleapis.com/auth/plus.me",
"https://www.googleapis.com/auth/plus.profile.emails.read")).setState("/user");
auth.setAccessType("offline");
response.addHeader("Place","Before");
response.sendRedirect(auth.build());
} catch (IOException e) {
e.printStackTrace();
}
} else {
response.addHeader("Place","After");
AuthorizationCodeResponseUrl authResponse = new AuthorizationCodeResponseUrl(transformName(request, 0));
// check for user-denied error
if (authResponse.getError() != null) {
logger.info("Denied");
} else {
try {
assert clientSecrets != null;
Calendar calendar = Calendar.getInstance();
NetHttpTransport net = new NetHttpTransport();
JacksonFactory jackson = new JacksonFactory();
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(net, jackson,
clientSecrets.getDetails().getClientId(), clientSecrets.getDetails().getClientSecret(),
authResponse.getCode(), transformName(request, 1))
.execute();
// Use access token to call API
GoogleCredential credential;
if (tokenResponse.getRefreshToken() == null) {
credential = new GoogleCredential();
credential.setFromTokenResponse(tokenResponse);
} else {
credential = createCredentialWithRefreshToken(net, jackson, clientSecrets, tokenResponse);
}
Plus plus =
new Plus.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
.setApplicationName("Google Plus Profile Info")
.build();
Person profile = plus.people().get("me").execute();
// Get profile info from ID token
GoogleIdToken idToken = tokenResponse.parseIdToken();
GoogleIdToken.Payload payload = idToken.getPayload();
User auth = new User();
auth.setAccessToken(tokenResponse.getAccessToken());
auth.setId(new BigInteger(payload.getSubject().trim())); // Use this value as a key to identify a user.
auth.setUuid(UUID.randomUUID().toString());
auth.setEmail(payload.getEmail());
auth.setVerifiedEmail(payload.getEmailVerified());
auth.setName(profile.getDisplayName());
auth.setPictureURL(profile.getImage().getUrl());
auth.setLocale(profile.getLanguage());
auth.setFamilyName(profile.getName().getFamilyName());
auth.setGivenName(profile.getName().getGivenName());
auth.setStatus(true);
auth.setExpired(false);
auth.setLocked(false);
auth.setExpiredCredentials(false);
auth.setRoles("USER");
auth.setRefreshToken(tokenResponse.getRefreshToken());
auth.setDateCreated(calendar.getTime());
calendar.add(Calendar.SECOND, toIntExact(tokenResponse.getExpiresInSeconds()));
auth.setExpirationDate(calendar.getTime());
auth.setDateModified(Calendar.getInstance().getTime());
Authentication authenticationToken = getOAuth2Token(auth);
request.authenticate(response);
if (//Validation happening) {
authenticationToken.setAuthenticated(true);
} else {
authenticationToken.setAuthenticated(false);
}
return authenticationToken;
} catch (TokenResponseException e) {
if (e.getDetails() != null) {
System.err.println("Error: " + e.getDetails().getError());
if (e.getDetails().getErrorDescription() != null) {
System.err.println(e.getDetails().getErrorDescription());
}
if (e.getDetails().getErrorUri() != null) {
System.err.println(e.getDetails().getErrorUri());
}
} else {
System.err.println(e.getMessage());
}
} catch (IOException | ServletException e) {
e.printStackTrace();
}
}
}
}else if(!context.getAuthentication().isAuthenticated()) {
setResponseUnauthenticated(response);
}else{
try {
response.sendRedirect(transformName(request,2)+"/user");
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
private void setResponseUnauthenticated(HttpServletResponse response){
try {
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
//create Json Object
JSONObject values = new JSONObject();
values.put("principal", null);
values.put("authentication", null);
values.put("timestamp", String.valueOf(Timestamp.from(Instant.now())));
values.put("code",401);
values.put("message", "Not Authorized");
out.print(values.toString());
} catch (JSONException | IOException e) {
e.printStackTrace();
}
}
public static GoogleCredential createCredentialWithRefreshToken(HttpTransport transport,
JsonFactory jsonFactory,
GoogleClientSecrets clientSecrets,
TokenResponse tokenResponse) {
return new GoogleCredential.Builder().setTransport(transport)
.setJsonFactory(jsonFactory)
.setClientSecrets(clientSecrets)
.build()
.setFromTokenResponse(tokenResponse);
}
public static String transformName(HttpServletRequest request, Integer type){
switch(type) {
case 0:
return request.getScheme() + "://" + // "http" + "://
request.getServerName() + // "myhost"
":" + // ":"
request.getServerPort() + // "8080"
request.getRequestURI() + // "/people"
"?" + // "?"
request.getQueryString(); // "lastname=Fox&age=30"
case 1:
return request.getScheme() + "://" + // "http" + "://
request.getServerName() + // "myhost"
":" + // ":"
request.getServerPort() + // "8080"
request.getRequestURI(); // "/people"
case 2:
return request.getScheme() + "://" + // "http" + "://
request.getServerName() + // "myhost"
":" + // ":"
request.getServerPort(); // "8080"
default:
return request.getScheme() + "://" + // "http" + "://
request.getServerName() + // "myhost"
":" + // ":"
request.getServerPort() + // "8080"
request.getRequestURI() + // "/people"
"?" + // "?"
request.getQueryString(); // "lastname=Fox&age=30"
}
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
response.sendRedirect(transformName(request,2)+"/user");
}
private CustomOAuth2AuthenticationToken getOAuth2Token(User auth) {
return new CustomOAuth2AuthenticationToken(auth);
}
private GoogleClientSecrets loadSecret(String name){
ClassPathResource resource = new ClassPathResource(name);
try {
// Exchange auth code for access token
return GoogleClientSecrets.load(JacksonFactory.getDefaultInstance(), new FileReader(resource.getFile()));
} catch (IOException e) {
return null;
}
}
}
我还将主要的安全类更改为:
private GoogleOAuth2Filter googleOAuth2Filter = new GoogleOAuth2Filter("/login/google");
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login/google", "/error**").permitAll().anyRequest().authenticated()
.and().exceptionHandling().authenticationEntryPoint((request, response, e) -> {
//create Json Object
try {
JSONObject values = new JSONObject();
values.put("principal", JSONObject.NULL);
values.put("authentication", JSONObject.NULL);
values.put("timestamp", String.valueOf(Timestamp.from(Instant.now())));
values.put("code",401);
values.put("message", "Not Authorized");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(values.toString());
} catch (JSONException | IOException f) {
f.printStackTrace();
}
})
.and().addFilterBefore(googleOAuth2Filter, BasicAuthenticationFilter.class);
// @formatter:on
}
我还为/user 和/logout 创建了自定义映射。
希望以后能帮到别人
关于Spring Boot + Spring Security + Spring OAuth2 + Google 登录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42727014/
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:
我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我
我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“
有没有办法让Ruby能够做这样的事情?classPlane@moved=0@x=0defx+=(v)#thisiserror@x+=v@moved+=1enddefto_s"moved#{@moved}times,currentxis#{@x}"endendplane=Plane.newplane.x+=5plane.x+=10putsplane.to_s#moved2times,currentxis15 最佳答案 您不能在Ruby中覆盖复合赋值运算符。任务在内部处理。您应该覆盖+,而不是+=。plane.a+=b与plane.a=
很高兴看到google代码:google-api-ruby-client项目,因为这对我来说意味着Ruby人员可以使用GoogleAPI-s来完善代码。虽然我现在很困惑,因为给出的唯一示例使用Buzz,并且根据我的实验,Google翻译(v2)api的行为必须与google-api-ruby-client中的Buzz完全不同。.我对“Explorer”演示示例很感兴趣——但据我所知,它并不是一个探索器。它所做的只是调用一个Buzz服务,然后浏览它已经知道的关于Buzz服务的事情。对我来说,Explorer应该让您“发现”所公开的服务和方法/功能,而不一定已经知道它们。我很想听听使用这个
出于某种原因,heroku尝试要求dm-sqlite-adapter,即使它应该在这里使用Postgres。请注意,这发生在我打开任何URL时-而不是在gitpush本身期间。我构建了一个默认的Facebook应用程序。gem文件:source:gemcuttergem"foreman"gem"sinatra"gem"mogli"gem"json"gem"httparty"gem"thin"gem"data_mapper"gem"heroku"group:productiondogem"pg"gem"dm-postgres-adapter"endgroup:development,:t
我是Ruby和这个网站的新手。下面两个函数是不同的,一个在函数外修改变量,一个不修改。defm1(x)x我想确保我理解正确-当调用m1时,对str的引用被复制并传递给将其视为x的函数。运算符当调用m2时,对str的引用被复制并传递给将其视为x的函数。运算符+创建一个新字符串,赋值x=x+"4"只是将x重定向到新字符串,而原始str变量保持不变。对吧?谢谢 最佳答案 String#+::str+other_str→new_strConcatenation—ReturnsanewStringcontainingother_strconc
我需要从站点抓取数据,但它需要我先登录。我一直在使用hpricot成功地抓取其他网站,但我是使用mechanize的新手,我真的对如何使用它感到困惑。我看到这个例子经常被引用:require'rubygems'require'mechanize'a=Mechanize.newa.get('http://rubyforge.org/')do|page|#Clicktheloginlinklogin_page=a.click(page.link_with(:text=>/LogIn/))#Submittheloginformmy_page=login_page.form_with(:act
我正在使用PostgreSQL9.1.3(x86_64-pc-linux-gnu上的PostgreSQL9.1.3,由gcc-4.6.real(Ubuntu/Linaro4.6.1-9ubuntu3)4.6.1,64位编译)和在ubuntu11.10上运行3.2.2或3.2.1。现在,我可以使用以下命令连接PostgreSQLsupostgres输入密码我可以看到postgres=#我将以下详细信息放在我的config/database.yml中并执行“railsdb”,它工作正常。开发:adapter:postgresqlencoding:utf8reconnect:falsedat