oauth协议简介
授权码模式流程:
授权码模式的特殊之处在于:用户同意授权的动作是在认证服务器上完成的,其他的模式是在第三方上完成的,这样避免了伪造用户授权。
授权码模式也是功能最完整,最严密的模式,也是使用最广泛的协议。
总结:就是为了不用将用户的用户名和密码交给第三方应用,就可以有权限用户存在服务器上的一些资源。
上图就是整个第三方登录的整个流程,Spring Social将这个流程封装到了SocialAuthenticationFilter中。
上图是整个第三方登录的代码流程
1、ServiceProvider:服务提供商的抽象,如使用qq,wx就需要提供不同的实现类
2、OAuth2Operations:封装了逻辑流程图中1-5步,因为1-5步是固定的,所以Spring Social提供了默认实现类OAuth2Template
3、Api :封装了第6步的服务提供商的信息
4、Connection:封装前6步获取到的用户信息。我们使用的就是OAuth2Connection。
5、ConnectionFactory: 创建Connection时,先调用serviceProvider去访问服务提供商接口,得到信息。
6、ApiAdapter:Connection的字段都是固定的,但是每个服务提供商提供的字段都是不一样的,在Connection和服务提供商提供的字段进行转换匹配,就需要使用ApiAdapter
7、UsersConnectionRepository:封装的Connection和自己业务中的user怎么一一对应呢?它们的对应关系存储在了UsersConnection这张表当中,使用UsersConnectionRepository进行查询。
@Data
public class QQUserInfo {
/**
* 返回码
*/
private String ret;
/**
* 如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。
*/
private String msg;
/**
*
*/
private String openId;
/**
* 不知道什么东西,文档上没写,但是实际api返回里有。
*/
private String is_lost;
/**
* 省(直辖市)
*/
private String province;
/**
* 市(直辖市区)
*/
private String city;
/**
* 出生年月
*/
private String year;
/**
* 用户在QQ空间的昵称。
*/
private String nickname;
/**
* 大小为30×30像素的QQ空间头像URL。
*/
private String figureurl;
/**
* 大小为50×50像素的QQ空间头像URL。
*/
private String figureurl_1;
/**
* 大小为100×100像素的QQ空间头像URL。
*/
private String figureurl_2;
/**
* 大小为40×40像素的QQ头像URL。
*/
private String figureurl_qq_1;
/**
* 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100×100的头像,但40×40像素则是一定会有。
*/
private String figureurl_qq_2;
/**
* 性别。 如果获取不到则默认返回”男”
*/
private String gender;
/**
* 标识用户是否为黄钻用户(0:不是;1:是)。
*/
private String is_yellow_vip;
/**
* 标识用户是否为黄钻用户(0:不是;1:是)
*/
private String vip;
/**
* 黄钻等级
*/
private String yellow_vip_level;
/**
* 黄钻等级
*/
private String level;
/**
* 标识是否为年费黄钻用户(0:不是; 1:是)
*/
private String is_yellow_year_vip;
}
public interface QQ {
QQUserInfo getQQUserInfo ();
}
public class QQImpl extends AbstractOAuth2ApiBinding implements QQ {
private String appId;
private String openid;
private static final String URL_GET_OPENID="https://graph.qq.com/oauth2.0/me?access_token=s%";
private static final String URL_GET_USERINFO="https://graph.qq.com/user/get_user_info?oauth_consumer_key=s%&openid=s%";
private ObjectMapper objectMapper=new ObjectMapper();
public QQImpl(String accessToken,String appId) {
super(accessToken,TokenStrategy.ACCESS_TOKEN_PARAMETER);
this.appId=appId;
String url=String.format(URL_GET_OPENID, accessToken);
String result=getRestTemplate().getForObject(url, String.class);
this.openid=StringUtils.substringBetween(result, "\"openid\"","}");
}
@Override
public QQUserInfo getQQUserInfo(){
String url=String.format(URL_GET_USERINFO, appId, openid);
String result=getRestTemplate().getForObject(url, String.class);
try {
return objectMapper.readValue(result, QQUserInfo.class);
} catch (Exception e) {
throw new RuntimeException("获取用户信息失败",e);
}
}
}
public class QQAdapter implements ApiAdapter<QQ> {
@Override
public boolean test(QQ api) {
return true;
}
@Override
public void setConnectionValues(QQ api, ConnectionValues values) {
QQUserInfo userInfo=api.getQQUserInfo();
values.setDisplayName(userInfo.getNickname());
values.setImageUrl(userInfo.getFigureurl_qq_1());
values.setProfileUrl(null);
values.setProviderUserId(userInfo.getOpenId());
}
@Override
public UserProfile fetchUserProfile(QQ api) {
return null;
}
@Override
public void updateStatus(QQ api, String message) {
}
}
public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ>{
private String appId;
private static final String URL_AUTHORIZE="https://graph.qq.com/oauth2.0/authorize";
private static final String URL_ACCESS_TOKEN="https://graph.qq.com/oauth2.0/token";
public QQServiceProvider(String appId,String appSecret) {
super(new OAuth2Template(appId, appSecret, URL_AUTHORIZE, URL_ACCESS_TOKEN));
}
@Override
public QQ getApi(String accessToken) {
return new QQImpl(accessToken, appId);
}
}
public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> {
public QQConnectionFactory(String providerId,String appId,String appSecret) {
super(providerId, new QQServiceProvider(appId, appSecret), new QQAdapter());
}
}
由于我用的mysql8.0所以提供的UserConnection建表语句,rank字段不能使用,只好重写JdbcConnectionRepository
public class RavenJdbcConnectionRepository implements ConnectionRepository {
private final String userId;
private final JdbcTemplate jdbcTemplate;
private final ConnectionFactoryLocator connectionFactoryLocator;
private final TextEncryptor textEncryptor;
private final String tablePrefix;
public RavenJdbcConnectionRepository(String userId, JdbcTemplate jdbcTemplate,
ConnectionFactoryLocator connectionFactoryLocator, TextEncryptor textEncryptor, String tablePrefix) {
this.userId = userId;
this.jdbcTemplate = jdbcTemplate;
this.connectionFactoryLocator = connectionFactoryLocator;
this.textEncryptor = textEncryptor;
this.tablePrefix = tablePrefix;
}
public MultiValueMap<String, Connection<?>> findAllConnections() {
List<Connection<?>> resultList = jdbcTemplate.query(
selectFromUserConnection() + " where userId = ? order by providerId, grade", connectionMapper, userId);
MultiValueMap<String, Connection<?>> connections = new LinkedMultiValueMap<String, Connection<?>>();
Set<String> registeredProviderIds = connectionFactoryLocator.registeredProviderIds();
for (String registeredProviderId : registeredProviderIds) {
connections.put(registeredProviderId, Collections.<Connection<?>>emptyList());
}
for (Connection<?> connection : resultList) {
String providerId = connection.getKey().getProviderId();
if (connections.get(providerId).size() == 0) {
connections.put(providerId, new LinkedList<Connection<?>>());
}
connections.add(providerId, connection);
}
return connections;
}
public List<Connection<?>> findConnections(String providerId) {
return jdbcTemplate.query(selectFromUserConnection() + " where userId = ? and providerId = ? order by grade",
connectionMapper, userId, providerId);
}
@SuppressWarnings("unchecked")
public <A> List<Connection<A>> findConnections(Class<A> apiType) {
List<?> connections = findConnections(getProviderId(apiType));
return (List<Connection<A>>) connections;
}
public MultiValueMap<String, Connection<?>> findConnectionsToUsers(MultiValueMap<String, String> providerUsers) {
if (providerUsers == null || providerUsers.isEmpty()) {
throw new IllegalArgumentException("Unable to execute find: no providerUsers provided");
}
StringBuilder providerUsersCriteriaSql = new StringBuilder();
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("userId", userId);
for (Iterator<Entry<String, List<String>>> it = providerUsers.entrySet().iterator(); it.hasNext();) {
Entry<String, List<String>> entry = it.next();
String providerId = entry.getKey();
providerUsersCriteriaSql.append("providerId = :providerId_").append(providerId)
.append(" and providerUserId in (:providerUserIds_").append(providerId).append(")");
parameters.addValue("providerId_" + providerId, providerId);
parameters.addValue("providerUserIds_" + providerId, entry.getValue());
if (it.hasNext()) {
providerUsersCriteriaSql.append(" or ");
}
}
List<Connection<?>> resultList = new NamedParameterJdbcTemplate(jdbcTemplate).query(selectFromUserConnection()
+ " where userId = :userId and " + providerUsersCriteriaSql + " order by providerId, grade", parameters,
connectionMapper);
MultiValueMap<String, Connection<?>> connectionsForUsers = new LinkedMultiValueMap<String, Connection<?>>();
for (Connection<?> connection : resultList) {
String providerId = connection.getKey().getProviderId();
List<String> userIds = providerUsers.get(providerId);
List<Connection<?>> connections = connectionsForUsers.get(providerId);
if (connections == null) {
connections = new ArrayList<Connection<?>>(userIds.size());
for (int i = 0; i < userIds.size(); i++) {
connections.add(null);
}
connectionsForUsers.put(providerId, connections);
}
String providerUserId = connection.getKey().getProviderUserId();
int connectionIndex = userIds.indexOf(providerUserId);
connections.set(connectionIndex, connection);
}
return connectionsForUsers;
}
public Connection<?> getConnection(ConnectionKey connectionKey) {
try {
return jdbcTemplate.queryForObject(
selectFromUserConnection() + " where userId = ? and providerId = ? and providerUserId = ?",
connectionMapper, userId, connectionKey.getProviderId(), connectionKey.getProviderUserId());
} catch (EmptyResultDataAccessException e) {
throw new NoSuchConnectionException(connectionKey);
}
}
@SuppressWarnings("unchecked")
public <A> Connection<A> getConnection(Class<A> apiType, String providerUserId) {
String providerId = getProviderId(apiType);
return (Connection<A>) getConnection(new ConnectionKey(providerId, providerUserId));
}
@SuppressWarnings("unchecked")
public <A> Connection<A> getPrimaryConnection(Class<A> apiType) {
String providerId = getProviderId(apiType);
Connection<A> connection = (Connection<A>) findPrimaryConnection(providerId);
if (connection == null) {
throw new NotConnectedException(providerId);
}
return connection;
}
@SuppressWarnings("unchecked")
public <A> Connection<A> findPrimaryConnection(Class<A> apiType) {
String providerId = getProviderId(apiType);
return (Connection<A>) findPrimaryConnection(providerId);
}
@Transactional
public void addConnection(Connection<?> connection) {
try {
ConnectionData data = connection.createData();
int grade = jdbcTemplate.queryForObject(
"select coalesce(max(grade) + 1, 1) as grade from " + tablePrefix
+ "UserConnection where userId = ? and providerId = ?",
new Object[] { userId, data.getProviderId() }, Integer.class);
jdbcTemplate.update("insert into " + tablePrefix
+ "UserConnection (userId, providerId, providerUserId, grade, displayName, profileUrl, imageUrl, accessToken, secret, refreshToken, expireTime) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
userId, data.getProviderId(), data.getProviderUserId(), grade, data.getDisplayName(),
data.getProfileUrl(), data.getImageUrl(), encrypt(data.getAccessToken()), encrypt(data.getSecret()),
encrypt(data.getRefreshToken()), data.getExpireTime());
} catch (DuplicateKeyException e) {
throw new DuplicateConnectionException(connection.getKey());
}
}
@Transactional
public void updateConnection(Connection<?> connection) {
ConnectionData data = connection.createData();
jdbcTemplate.update("update " + tablePrefix
+ "UserConnection set displayName = ?, profileUrl = ?, imageUrl = ?, accessToken = ?, secret = ?, refreshToken = ?, expireTime = ? where userId = ? and providerId = ? and providerUserId = ?",
data.getDisplayName(), data.getProfileUrl(), data.getImageUrl(), encrypt(data.getAccessToken()),
encrypt(data.getSecret()), encrypt(data.getRefreshToken()), data.getExpireTime(), userId,
data.getProviderId(), data.getProviderUserId());
}
@Transactional
public void removeConnections(String providerId) {
jdbcTemplate.update("delete from " + tablePrefix + "UserConnection where userId = ? and providerId = ?", userId,
providerId);
}
@Transactional
public void removeConnection(ConnectionKey connectionKey) {
jdbcTemplate.update(
"delete from " + tablePrefix
+ "UserConnection where userId = ? and providerId = ? and providerUserId = ?",
userId, connectionKey.getProviderId(), connectionKey.getProviderUserId());
}
// internal helpers
private String selectFromUserConnection() {
return "select userId, providerId, providerUserId, displayName, profileUrl, imageUrl, accessToken, secret, refreshToken, expireTime from "
+ tablePrefix + "UserConnection";
}
private Connection<?> findPrimaryConnection(String providerId) {
List<Connection<?>> connections = jdbcTemplate.query(
selectFromUserConnection() + " where userId = ? and providerId = ? order by grade", connectionMapper,
userId, providerId);
if (connections.size() > 0) {
return connections.get(0);
} else {
return null;
}
}
private final ServiceProviderConnectionMapper connectionMapper = new ServiceProviderConnectionMapper();
private final class ServiceProviderConnectionMapper implements RowMapper<Connection<?>> {
public Connection<?> mapRow(ResultSet rs, int rowNum) throws SQLException {
ConnectionData connectionData = mapConnectionData(rs);
ConnectionFactory<?> connectionFactory = connectionFactoryLocator
.getConnectionFactory(connectionData.getProviderId());
return connectionFactory.createConnection(connectionData);
}
private ConnectionData mapConnectionData(ResultSet rs) throws SQLException {
return new ConnectionData(rs.getString("providerId"), rs.getString("providerUserId"),
rs.getString("displayName"), rs.getString("profileUrl"), rs.getString("imageUrl"),
decrypt(rs.getString("accessToken")), decrypt(rs.getString("secret")),
decrypt(rs.getString("refreshToken")), expireTime(rs.getLong("expireTime")));
}
private String decrypt(String encryptedText) {
return encryptedText != null ? textEncryptor.decrypt(encryptedText) : encryptedText;
}
private Long expireTime(long expireTime) {
return expireTime == 0 ? null : expireTime;
}
}
private <A> String getProviderId(Class<A> apiType) {
return connectionFactoryLocator.getConnectionFactory(apiType).getProviderId();
}
private String encrypt(String text) {
return text != null ? textEncryptor.encrypt(text) : text;
}
}
public class RavenJdbcUsersConnectionRepository implements UsersConnectionRepository {
private final JdbcTemplate jdbcTemplate;
private final ConnectionFactoryLocator connectionFactoryLocator;
private final TextEncryptor textEncryptor;
private ConnectionSignUp connectionSignUp;
private String tablePrefix = "";
public RavenJdbcUsersConnectionRepository(DataSource dataSource, ConnectionFactoryLocator connectionFactoryLocator,
TextEncryptor textEncryptor) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.connectionFactoryLocator = connectionFactoryLocator;
this.textEncryptor = textEncryptor;
}
/**
* The command to execute to create a new local user profile in the event no
* user id could be mapped to a connection. Allows for implicitly creating a
* user profile from connection data during a provider sign-in attempt. Defaults
* to null, indicating explicit sign-up will be required to complete the
* provider sign-in attempt.
*
* @param connectionSignUp a {@link ConnectionSignUp} object
* @see #findUserIdsWithConnection(Connection)
*/
public void setConnectionSignUp(ConnectionSignUp connectionSignUp) {
this.connectionSignUp = connectionSignUp;
}
/**
* Sets a table name prefix. This will be prefixed to all the table names before
* queries are executed. Defaults to "". This is can be used to qualify the
* table name with a schema or to distinguish Spring Social tables from other
* application tables.
*
* @param tablePrefix the tablePrefix to set
*/
public void setTablePrefix(String tablePrefix) {
this.tablePrefix = tablePrefix;
}
public List<String> findUserIdsWithConnection(Connection<?> connection) {
ConnectionKey key = connection.getKey();
List<String> localUserIds = jdbcTemplate.queryForList(
"select userId from " + tablePrefix + "UserConnection where providerId = ? and providerUserId = ?",
String.class, key.getProviderId(), key.getProviderUserId());
if (localUserIds.size() == 0 && connectionSignUp != null) {
String newUserId = connectionSignUp.execute(connection);
if (newUserId != null) {
createConnectionRepository(newUserId).addConnection(connection);
return Arrays.asList(newUserId);
}
}
return localUserIds;
}
public Set<String> findUserIdsConnectedTo(String providerId, Set<String> providerUserIds) {
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("providerId", providerId);
parameters.addValue("providerUserIds", providerUserIds);
final Set<String> localUserIds = new HashSet<String>();
return new NamedParameterJdbcTemplate(jdbcTemplate).query(
"select userId from " + tablePrefix
+ "UserConnection where providerId = :providerId and providerUserId in (:providerUserIds)",
parameters, new ResultSetExtractor<Set<String>>() {
public Set<String> extractData(ResultSet rs) throws SQLException, DataAccessException {
while (rs.next()) {
localUserIds.add(rs.getString("userId"));
}
return localUserIds;
}
});
}
public ConnectionRepository createConnectionRepository(String userId) {
if (userId == null) {
throw new IllegalArgumentException("userId cannot be null");
}
return new RavenJdbcConnectionRepository(userId, jdbcTemplate, connectionFactoryLocator, textEncryptor,
tablePrefix);
}
}
public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> {
public QQConnectionFactory(String providerId,String appId,String appSecret) {
super(providerId, new QQServiceProvider(appId, appSecret), new QQAdapter());
}
}
@Configuration
@ConditionalOnProperty(prefix = "fuiou.security.social.qq",name = "app-id")
public class QQAutoConfig extends SocialAutoConfigurerAdapter {
@Autowired
private SecurityProperties securityProperties;
@Override
protected ConnectionFactory<?> createConnectionFactory() {
return new QQConnectionFactory(securityProperties.getSocial().getQq().getProviderId(),
securityProperties.getSocial().getQq().getAppId(),
securityProperties.getSocial().getQq().getAppSecret());
}
}
@Configuration
@EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
private SecurityProperties securityProperties;
@Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
// 插入到UsersConnection加解密。Encryptors.noOpText()这里不做加解密
return new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
}
@Bean
public SpringSocialConfigurer fuiouSocialSecurityConfig() {
return new FuiouSpringSocialConfigurer(securityProperties.getSocial().getFilterProcessesUrl());
}
}
@Setter
@Getter
public class SocialProperties {
private String filterProcessesUrl="/auth";
private QQProperties qq=new QQProperties();
}
@Setter
@Getter
public class QQProperties extends SocialProperties {
private String providerId="qq";
}
browser项目引入SocialConfig
@Autowired
private SpringSocialConfigurer fuiouSocialSecurityConfig;
http
.apply(fuiouSocialSecurityConfig)
fuiou.security.social.qq.app-id=101364240
fuiou.security.social.qq.app-secret=ef27b7a6ca651a3609dd47f21e385955
fuiou.security.social.filterProcessesUrl=/login
fuiou.security.social.qq.providerId=qq
server.port=80
app-id和app-secret转载自: https://www.jianshu.com/p/48bc3173663e
点击登录
打印的日志可以看到:引发跳转的请求是===>http://127.0.0.1/signin
跳转到了signin接口。
原因:
@SuppressWarnings("unchecked")
protected AccessGrant postForAccessGrant(String accessTokenUrl, MultiValueMap<String, String> parameters) {
return extractAccessGrant(getRestTemplate().postForObject(accessTokenUrl, parameters, Map.class));
}
protected RestTemplate createRestTemplate() {
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactorySelector.getRequestFactory();
RestTemplate restTemplate = new RestTemplate(requestFactory);
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>(2);
converters.add(new FormHttpMessageConverter());
converters.add(new FormMapHttpMessageConverter());
converters.add(new MappingJackson2HttpMessageConverter());
restTemplate.setMessageConverters(converters);
restTemplate.setErrorHandler(new LoggingErrorHandler());
if (!useParametersForClientAuthentication) {
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (interceptors == null) { // defensively initialize list if it is null. (See SOCIAL-430)
interceptors = new ArrayList<ClientHttpRequestInterceptor>();
restTemplate.setInterceptors(interceptors);
}
interceptors.add(new PreemptiveBasicAuthClientHttpRequestInterceptor(clientId, clientSecret));
}
return restTemplate;
}
OAuth2AuthenticationService在去换取令牌时获取到的返回值是text/html模式,但是在构建与提供者进行API通信的RestTemplate时未添加text/html类型的转换器
创建自己的OAuth2Template,并重写方法
@Slf4j
public class QQOAuth2Template extends OAuth2Template {
public QQOAuth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) {
super(clientId, clientSecret, authorizeUrl, accessTokenUrl);
setUseParametersForClientAuthentication(true);
}
@Override
protected RestTemplate createRestTemplate() {
RestTemplate restTemplate= super.createRestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
return restTemplate;
}
@Override
protected AccessGrant postForAccessGrant(String accessTokenUrl, MultiValueMap<String, String> parameters) {
String responseStr=getRestTemplate().postForObject(accessTokenUrl, parameters, String.class);
log.info("responseStr==>"+responseStr);
String[] items=StringUtils.splitByWholeSeparatorPreserveAllTokens(responseStr, "&");
String accessToken=StringUtils.substringAfter(items[0], "=");
Long expiresIn=new Long(StringUtils.substringAfter(items[1], "="));
String refreshToken=StringUtils.substringAfter(items[2], "=");
return new AccessGrant(accessToken, null, refreshToken, expiresIn);
}
}
发现跳转signup:引发跳转的请求是===>http://127.0.0.1/signup
原因:这是因为它根据qq返回的信息去userconnection 中匹配,发现并没有数据,会跳转到注册页面。
在Properties中配置注册页面url
@Data
public class BrowserProperties {
private String loginPage = "/fuiou-login.html";
private String siginUpUrl = "/fuiou-siginUp.html";
private LoginType loginType=LoginType.JSON;
private int rememberMeSeconds = 3600;
}
改变默认的注册地址
@Bean
public SpringSocialConfigurer fuiouSocialSecurityConfig() {
FuiouSpringSocialConfigurer configurer=new FuiouSpringSocialConfigurer(securityProperties.getSocial().getFilterProcessesUrl());
configurer.signupUrl(securityProperties.getBrowser().getSiginUpUrl());
return configurer;
}
注册的jsp
<form action="/regist" method="post">
<table>
<tr>
<td>用户名</td>
<td><input name="username" /></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
<td colspan="2">
<button type="submit" name="type" value="regist">注册</button>
<button type="submit" name="type" value="binding">绑定</button>
</td>
</tr>
</table>
</form>
排除对注册页的拦截
.antMatchers("/authentication/requrie",
securityProperties.getBrowser().getLoginPage(),
"/code/*",securityProperties.getBrowser().getSiginUpUrl())
.permitAll()//该url不需要身份认证
发现改完之后,虽然userconnection增加了数据但是还是跳转到了登录页
原因:debug发现usersConnectionRepository.findUserIdsWithConnection的实现类没有用RavenJdbcUsersConnectionRepository,而是用的InMemoryUsersConnectionRepository,去内存中找自然是找不到了。
protected String toUserId(Connection<?> connection) {
List<String> userIds = usersConnectionRepository.findUserIdsWithConnection(connection);
// only if a single userId is connected to this providerUserId
return (userIds.size() == 1) ? userIds.iterator().next() : null;
}
修改:
@Primary
@Bean
@Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
// 插入到UsersConnection加解密。Encryptors.noOpText()这里不做加解密
RavenJdbcUsersConnectionRepository usersConnectionRepository= new RavenJdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
return usersConnectionRepository;
}
这样还是有一个问题:第一次用微信登录的用户还是要注册一遍。
实现自动注册
@Component
public class DemoConnectionSignUp implements ConnectionSignUp {
@Override
public String execute(Connection<?> connection) {
// 根据社交用户信息默认创建用户并返回唯一标识
//这里直接用昵称了,实际情况要根据需求改
return connection.getDisplayName();
}
}
@Autowired(required = false)
private ConnectionSignUp connectionSignUp;
@Primary
@Bean
@Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
// 插入到UsersConnection加解密。Encryptors.noOpText()这里不做加解密
RavenJdbcUsersConnectionRepository usersConnectionRepository= new RavenJdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
if(connectionSignUp!=null) {
usersConnectionRepository.setConnectionSignUp(connectionSignUp);
}
return usersConnectionRepository;
}