cas3.5.3添加微博登陆(操作笔记)
来源:互联网 发布:淘宝手工护肤品良心店 编辑:程序博客网 时间:2024/05/20 14:22
cas3.5.3添加微博登陆(操作笔记)
1.pom文件引入 cas-server-support-oauth 的jar包
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-support-oauth</artifactId>
<version>3.5.3</version>
</dependency>
2.创建一个枚举类来区分是哪种oauth2.0的协议(微博、QQ、微信)
public enum Oauth20Type {
/**
* QQ平台
*/
QQ,
/**
* 微信平台
*/
WEIXIN,
/**
* 新浪微博
*/
SINA
}
3. 由于我们所采用的注册业务不在cas服务器,所以要存放注册时需要的必要信息
public class Oauth2Info{
/**
*
*/
/**
* 如果用户已存在,此id存放用户在本系统数据库中的id
*/
public static final String ID="id";
/**
* Author2.0获取到的id
*/
public static final String OPENID="openId";
/**
* Author2.0获取到的用户名
*/
public static final String NAME="name";
/**
* Author2.0获取到的图片信息
*/
public static final String IMAGE="image";
/**
* Author2.0的类型(QQ 微博 微信一类的)
*/
public static final String TYPE="type";
/**
* 判断用户是否在数据库存在
*/
public static final String EXIST="exist";
}
4.在cas单点登陆的登陆界面添加微博登陆的超链接
当访问cas服务器的login时,会触发spring的web-flow配置文件login-webflow.xml,此时我们需要一个action来处理我们的oauth2.0的请求
修改login-webflow.xml
添加此配置放置入口on-start标签之后
<!-- 新浪微博 -->
<action-state id="MyOAuthAction">
<evaluate expression="MyOAuthAction" />
<transition on="success" to="sendTicketGrantingTicket" />
<transition on="register" to="redirectRegisterView" />
<transition on="error" to="ticketGrantingTicketExistsCheck" />
</action-state>
//这个是重定向客服端的注册页面
<end-state id="redirectRegisterView" view="externalRedirect:${oauthUniversalVerificationUrl}" />
①先在cas-servlet.xml里面配置我们的action
<!-- 新浪微博 -->
<bean id="MyOAuthAction" class="包名.myOAuthAction"
p:centralAuthenticationService-ref="centralAuthenticationService" >
<property name="configuration" ref="oauthConfiguration">
</property>
</bean>
具体类如下:
/**
* 因原逻辑不符合我们的业务需求 改写,
* 之前只有success fail,
* 现在有 success fail register
*/
public class MyOAuthAction extends AbstractAction{
private final Logger log = LoggerFactory.getLogger(OAuthAction.class);
@NotNull
private OAuthConfiguration configuration;
@NotNull
private CentralAuthenticationService centralAuthenticationService;
private String oauth10loginUrl = "/" + OAuthConstants.OAUTH10_LOGIN_URL;
@Override
protected Event doExecute(final RequestContext context) throws Exception {
final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
final HttpSession session = request.getSession();
// get provider type
final String providerType = request.getParameter(OAuthConstants.OAUTH_PROVIDER);
log.debug("providerType : {}", providerType);
// it's an authentication
if (StringUtils.isNotBlank(providerType)) {
// get provider
final OAuthProvider provider = OAuthUtils
.getProviderByType(this.configuration.getProviders(), providerType);
log.debug("provider : {}", provider);
// get credential
@SuppressWarnings("unchecked")
final OAuthCredential credential = provider.getCredential(new HttpUserSession(request),
request.getParameterMap());
log.debug("credential : {}", credential);
// retrieve parameters from web session
final Service service = (Service) session.getAttribute(OAuthConstants.SERVICE);
context.getFlowScope().put(OAuthConstants.SERVICE, service);
restoreRequestAttribute(request, session, OAuthConstants.THEME);
restoreRequestAttribute(request, session, OAuthConstants.LOCALE);
restoreRequestAttribute(request, session, OAuthConstants.METHOD);
// create credentials
final OAuthCredentials credentials = new OAuthCredentials(credential);
try {
WebUtils.putTicketGrantingTicketInRequestScope(context, this.centralAuthenticationService
.createTicketGrantingTicket(credentials));
Map<String, Object> attributes = credentials.getUserProfile().getAttributes();
//这个是判断该用户是否已存在我们的数据,存在返回 sucess 不存在我们构造客户端需要的参数重定向客户端并带上参数(这样有点安全问题,有更好的解决方案的可以联系QQ419499249)
if(attributes.get(Oauth2Info.EXIST)==null||!(Boolean)(attributes.get(Oauth2Info.EXIST))){
//存放的子系统的注册页面的信息
Object registerServiceUrl=session.getAttribute(ConstantsUtil.MY_REGISTER);
String url=(String)registerServiceUrl;
if(url==null || url.trim().isEmpty()||getUrlParam(attributes)==null){
return error();
}
//这里把重定向的url进行拼接并放进flow域中 属性名为oauthUniversalVerificationUrl
context.getFlowScope().put("oauthUniversalVerificationUrl", url.trim()+"?"+getUrlParam(attributes));
return result("register");
}
return success();
} catch (final TicketException e) {
//这里异常说明是在解析过程中出错 其实就是oauth2.0认证失败
return error();
}
} else {
// no authentication : go to login page
// save parameters in web session
final Service service = (Service) context.getFlowScope().get(OAuthConstants.SERVICE);
if (service != null) {
session.setAttribute(OAuthConstants.SERVICE, service);
}
//ConstantsUtil
//存放客户端给的注册链接供后面跳回客户端的注册页面使用
final String registerServiceUrl = request.getParameter(ConstantsUtil.MY_REGISTER);
if (StringUtils.trimToNull(registerServiceUrl)!=null) {
session.setAttribute(ConstantsUtil.MY_REGISTER, registerServiceUrl.trim());
}
saveRequestParameter(request, session, OAuthConstants.THEME);
saveRequestParameter(request, session, OAuthConstants.LOCALE);
saveRequestParameter(request, session, OAuthConstants.METHOD);
//这里是生成oauth2.0的各种登陆的链接的
// for all providers, generate authorization urls
for (final OAuthProvider provider : this.configuration.getProviders()) {
//这个就是页面通过${自己的provider类名Url } 这样页面就会自动生成登陆url
final String key = provider.getType() + "Url";
String authorizationUrl = null;
// for OAuth 1.0 protocol, delay request_token request by pointing to an intermediate url
if (provider instanceof BaseOAuth10Provider) {
authorizationUrl = OAuthUtils.addParameter(request.getContextPath() + this.oauth10loginUrl,
OAuthConstants.OAUTH_PROVIDER, provider.getType());
} else {
authorizationUrl = provider.getAuthorizationUrl(new HttpUserSession(session));
}
log.debug("{} -> {}", key, authorizationUrl);
context.getFlowScope().put(key, authorizationUrl);
}
}
return error();
}
/**
* Restore an attribute in web session as an attribute in request.
*
* @param request
* @param session
* @param name
*/
private void restoreRequestAttribute(final HttpServletRequest request, final HttpSession session, final String name) {
final String value = (String) session.getAttribute(name);
request.setAttribute(name, value);
}
/**
* Save a request parameter in the web session.
*
* @param request
* @param session
* @param name
*/
private void saveRequestParameter(final HttpServletRequest request, final HttpSession session, final String name) {
final String value = request.getParameter(name);
if (value != null) {
session.setAttribute(name, value);
}
}
public void setCentralAuthenticationService(final CentralAuthenticationService centralAuthenticationService) {
this.centralAuthenticationService = centralAuthenticationService;
}
public void setOauth10loginUrl(final String oauth10loginUrl) {
this.oauth10loginUrl = oauth10loginUrl;
}
public void setConfiguration(final OAuthConfiguration configuration) {
this.configuration = configuration;
for (final OAuthProvider provider : configuration.getProviders()) {
final BaseOAuthProvider baseProvider = (BaseOAuthProvider) provider;
// calculate new callback url by adding the OAuth provider type to the login url
baseProvider.setCallbackUrl(OAuthUtils.addParameter(configuration.getLoginUrl(),
OAuthConstants.OAUTH_PROVIDER, provider.getType()));
}
}
private String getUrlParam(Map<String,Object> map){
//需要传送的信息 name openid image type
StringBuffer param=new StringBuffer();
String name=(String)map.get(Oauth2Info.NAME);
if(StringUtils.trimToNull(name)!=null){
try {
name=URLEncoder.encode(name, "utf-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
String openid=(String)map.get(Oauth2Info.OPENID);
String image=(String)map.get(Oauth2Info.IMAGE);
Oauth20Type type=(Oauth20Type)map.get(Oauth2Info.TYPE);
param.append("openid=").append(openid).append("&")
.append("type=").append(type).append("&")
.append("name=").append(name).append("&")
.append("image=").append(image).append("&");
return param.substring(0, param.length()-1);
}
}
在你自己的cas的登陆页面加入超链接
<a href="${SinaWeiboProviderUrl }">微博登陆</a>
ConstantsUtil类如下:
/**
* 常量
*/
public class ConstantsUtil {
public final static String MY_REGISTER = "registerUrl";
}
②需要注入一个configuration我们在deployerConfigContext.xml里配置一个
<!-- 新浪微博配置 -->
<bean id="oauthConfiguration" class="org.jasig.cas.support.oauth.OAuthConfiguration">
<property name="providers">
<list>
<ref bean="sinaWeibo" />
</list>
</property>
<property name="loginUrl" value="${cas.securityContext.casProcessingFilterEntryPoint.loginUrl}"></property>
</bean>
③此配置需要一个providers集合 我们目前只配置了一个新浪的,在applicationContext.xml添加如下配置
<!-- 新浪微博 -->
<bean id="sinaWeibo" class="包名.SinaWeiboProvider">
<property name="key" value="key" />
<property name="secret" value="secret" />
<!-- 下面的callbackUrl 在测试中无效,会自动生成 回跳后的cas地址取自配置 cas.properties里面的server.name -->
<property name="callbackUrl" value="https://api.weibo.com/oauth2/default.html" />
</bean>
此类的具体代码如下:
添加新浪api类
/**
*新浪微博的api
*/
public class SinaWeiboApi20 extends DefaultApi20
{
private static final String AUTHORIZE_URL = "https://api.weibo.com/oauth2/authorize?client_id=%s&redirect_uri=%s&response_type=code";
private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s";
@Override
public Verb getAccessTokenVerb()
{
return Verb.POST;
}
@Override
public AccessTokenExtractor getAccessTokenExtractor()
{
return new JsonTokenExtractor();
}
@Override
public String getAccessTokenEndpoint()
{
return "https://api.weibo.com/oauth2/access_token?grant_type=authorization_code";
}
@Override
public String getAuthorizationUrl(OAuthConfig config)
{
// Append scope if present
if (config.hasScope())
{
return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback()), OAuthEncoder.encode(config.getScope()));
}
else
{
return String.format(AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback()));
}
}
}
/**
* 新浪微博Provider
*/
public class SinaWeiboProvider extends MyBaseOauth20Provider {
private String userInfoUrl="https://api.weibo.com/2/users/show.json";
@Override
protected String getProfileUrl() {
return "https://api.weibo.com/2/account/get_uid.json";
}
@Override
public String getOpenId(String body) {
String jsonBody="["+body+"]";
JSONArray jsonArray = JSONArray.fromObject(jsonBody);
JSONObject object = (JSONObject)jsonArray.get(0);
return object.getString("uid");
}
@Override
public Oauth20Type getOauth20Type() {
return Oauth20Type.SINA;
}
@Override
public String getOpenName(String uid, Token accessToken,JSONArray jsonArray) {
String openName=null;
String body=null;
if(jsonArray==null){
String newUserInfoUrl=userInfoUrl+"?uid="+uid+"&access_token="+accessToken.getToken();
try {
body = "["+CommonsUtils.getJson(newUserInfoUrl)+"]";
jsonArray=JSONArray.fromObject(body);
} catch (Exception e) {
e.printStackTrace();
}
}
try {
JSONObject object = (JSONObject)jsonArray.get(0);
openName=object.getString("name");
} catch (Exception e) {
e.printStackTrace();
}
return openName;
}
@Override
public String getOpenImageUrl(String uid, Token accessToken,JSONArray jsonArray) {
String openImageUrl=null;
String body=null;
if(jsonArray==null){
String newUserInfoUrl=userInfoUrl+"?uid="+uid+"&access_token="+accessToken.getToken();
try {
body = "["+CommonsUtils.getJson(newUserInfoUrl)+"]";
jsonArray=JSONArray.fromObject(body);
} catch (Exception e) {
e.printStackTrace();
}
}
try {
JSONObject object = (JSONObject)jsonArray.get(0);
openImageUrl=object.getString("profile_image_url");
} catch (Exception e) {
e.printStackTrace();
}
return openImageUrl;
}
}
父类代码如下:
/**
* 将 oauth2.0 验证的相同的地方抽取出来
*/
public abstract class MyBaseOauth20Provider extends BaseOAuth20Provider{
String scope=null;
@Override
protected void internalInit() {
if (scope != null) {
service = new ServiceBuilder().provider(SinaWeiboApi20.class).apiKey(key)
.apiSecret(secret).callback(callbackUrl).scope(scope).build();
} else {
service = new ServiceBuilder().provider(SinaWeiboApi20.class).apiKey(key)
.apiSecret(secret).callback(callbackUrl).build();
}
}
@Override
protected UserProfile extractUserProfile(String body) {
UserProfile userProfile = new UserProfile();
String openId = getOpenId(body);
if(StringUtils.trimToNull(openId)!=null){
userProfile.setId(openId);
//这里添加属性是为了后面的拼接参数
userProfile.addAttribute(Oauth2Info.OPENID, openId);
}
//这里添加属性是为了后面的拼接参数 和 数据库查询的时候区分类别
userProfile.addAttribute(Oauth2Info.TYPE, getOauth20Type());
return userProfile;
}
@Override
protected UserProfile getUserProfile(final Token accessToken) {
final String body = sendRequestForData(accessToken, getProfileUrl());
if (body == null) {
return null;
}
final UserProfile profile = extractUserProfile(body);
addAccessTokenToProfile(profile, accessToken);
//这个是为了当一个请求可以获取所有需要的数据时避免二次访问
JSONArray jsonArray=null;
//这里添加属性是为了后面的拼接参数
profile.addAttribute(Oauth2Info.NAME, getOpenName(profile.getId(),accessToken,jsonArray));
profile.addAttribute(Oauth2Info.IMAGE, getOpenImageUrl(profile.getId(),accessToken,jsonArray));
return profile;
}
/**
* 解析数据获取aouth2.0 对应系统的用户名称
* @param body
* @return
*/
public abstract String getOpenName(String uid, Token accessToken,JSONArray jsonArray);
/**
* 解析数据获取aouth2.0 对应系统的图片的网络路径
* @param body
* @return
*/
public abstract String getOpenImageUrl(String uid, Token accessToken,JSONArray jsonArray);
/**
* 解析数据获取openId
* @param body
* @return
*/
public abstract String getOpenId(String body);
/**
* 获取oauth2.0的类型
* @return
*/
public abstract Oauth20Type getOauth20Type();
/**
* 克隆的时候用到 需要的话自己实现
*/
protected BaseOAuthProvider newProvider() {
// TODO Auto-generated method stub
return null;
}
}
CommonsUtils类如下:
public class CommonsUtils {
/**
* 通过url链接获取json数据
*/
public static String getJson(String url) throws Exception {
String data = "";// 访问返回结果
BufferedReader read = null;// 读取访问结果
try {
// 创建url
URL realurl = new URL(url);
// 打开连接
URLConnection connection = realurl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
// 建立连接
connection.connect();
// 获取所有响应头字段
Map<String, List<String>> map = connection.getHeaderFields();
// 遍历所有的响应头字段,获取到cookies等
// 定义 BufferedReader输入流来读取URL的响应
read = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;// 循环读取
while ((line = read.readLine()) != null) {
data += line;
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
} finally {
if (read != null) {// 关闭流
try {
read.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return data;
}
}
5.添加我们自己的数据库校验,已存在,登陆成功,不存在,重定向注册界面
修改deployerConfigContext.xml
①在name="credentialsToPrincipalResolvers"的list节点添加
<!-- 新浪微博 -->
<bean class="org.jasig.cas.support.oauth.authentication.principal.OAuthCredentialsToPrincipalResolver" >
<property name="attributeRepository" ref="oauthAttributeRepository" />
</bean>
②在deployerConfigContext.xml添加如下bean
<!-- 新浪微博 -->
<bean id="oauthAttributeRepository"
class="org.jasig.services.persondir.support.StubPersonAttributeDao">
</bean>
③在name="authenticationHandlers"的list节点添加
<!-- 新浪微博 -->
<bean class="包名.Oauth20QueryDatabaseAuthenticationHandler">
<property name="configuration" ref="oauthConfiguration"/>
<property name="dataSource" ref="dataSource" />
<property name="passwordEncoder" ref="passwordEncoder"/>
<property name="querySql" value="select 字段 from 表名 o where 1=1 "/>
</bean>
Oauth20QueryDatabaseAuthenticationHandler类如下:
/**
* 通过继承 改写此类 从数据库获取用户信息,
* 获取成功说明此用户已经在本系统了,获取失败说明此用户未关联本系统的账号
*/
public class Oauth20QueryDatabaseAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler{
@NotNull
private OAuthConfiguration configuration;
@NotNull
private JdbcTemplate jdbcTemplate;
@NotNull
private DataSource dataSource;
@NotNull
private String querySql;
@NotNull
private PasswordEncoder passwordEncoder = new PlainTextPasswordEncoder();
@Override
public boolean supports(final Credentials credentials) {
return credentials != null && (OAuthCredentials.class.isAssignableFrom(credentials.getClass()));
}
@Override
protected boolean doAuthentication(final Credentials credentials) throws AuthenticationException {
final OAuthCredentials oauthCredentials = (OAuthCredentials) credentials;
log.debug("credential : {}", oauthCredentials);
final String providerType = oauthCredentials.getCredential().getProviderType();
log.debug("providerType : {}", providerType);
// get provider
final OAuthProvider provider = OAuthUtils.getProviderByType(this.configuration.getProviders(), providerType);
log.debug("provider : {}", provider);
// get user profile
final UserProfile userProfile = provider.getUserProfile(oauthCredentials.getCredential());
log.debug("userProfile : {}", userProfile);
if (userProfile != null && StringUtils.isNotBlank(userProfile.getId())) {
oauthCredentials.setUserProfile(userProfile);
String sql=querySql+" and o.open_id = ? and o.system_type = '"+userProfile.getAttributes().get(Oauth2Info.TYPE)+"'";
//通过 openid 和 类型查询看数据库是否有数据
String memberId=null;
try {
memberId = getJdbcTemplate().queryForObject(sql, String.class, userProfile.getId());
} catch (DataAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(!StringUtils.isEmpty(memberId)){
//有则说明该用户已经在本系统存在
userProfile.addAttribute(Oauth2Info.EXIST, true);
userProfile.addAttribute(Oauth2Info.ID, memberId);
}
return true;
} else {
return false;
}
}
public void setConfiguration(final OAuthConfiguration configuration) {
this.configuration = configuration;
}
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.dataSource = dataSource;
}
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
public void setQuerySql(String querySql) {
this.querySql = querySql;
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
}
④在id="authenticationManager" 节点下添加此配置,作用是认证成功后客户端可以拿到认证后的数据
<!-- 新浪微博 -->
<property name="authenticationMetaDataPopulators">
<list>
<bean class="org.jasig.cas.support.oauth.authentication.OAuthAuthenticationMetaDataPopulator" />
</list>
</property>
1.pom文件引入 cas-server-support-oauth 的jar包
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-support-oauth</artifactId>
<version>3.5.3</version>
</dependency>
2.创建一个枚举类来区分是哪种oauth2.0的协议(微博、QQ、微信)
public enum Oauth20Type {
/**
* QQ平台
*/
QQ,
/**
* 微信平台
*/
WEIXIN,
/**
* 新浪微博
*/
SINA
}
3. 由于我们所采用的注册业务不在cas服务器,所以要存放注册时需要的必要信息
public class Oauth2Info{
/**
*
*/
/**
* 如果用户已存在,此id存放用户在本系统数据库中的id
*/
public static final String ID="id";
/**
* Author2.0获取到的id
*/
public static final String OPENID="openId";
/**
* Author2.0获取到的用户名
*/
public static final String NAME="name";
/**
* Author2.0获取到的图片信息
*/
public static final String IMAGE="image";
/**
* Author2.0的类型(QQ 微博 微信一类的)
*/
public static final String TYPE="type";
/**
* 判断用户是否在数据库存在
*/
public static final String EXIST="exist";
}
4.在cas单点登陆的登陆界面添加微博登陆的超链接
当访问cas服务器的login时,会触发spring的web-flow配置文件login-webflow.xml,此时我们需要一个action来处理我们的oauth2.0的请求
修改login-webflow.xml
添加此配置放置入口on-start标签之后
<!-- 新浪微博 -->
<action-state id="MyOAuthAction">
<evaluate expression="MyOAuthAction" />
<transition on="success" to="sendTicketGrantingTicket" />
<transition on="register" to="redirectRegisterView" />
<transition on="error" to="ticketGrantingTicketExistsCheck" />
</action-state>
//这个是重定向客服端的注册页面
<end-state id="redirectRegisterView" view="externalRedirect:${oauthUniversalVerificationUrl}" />
①先在cas-servlet.xml里面配置我们的action
<!-- 新浪微博 -->
<bean id="MyOAuthAction" class="包名.myOAuthAction"
p:centralAuthenticationService-ref="centralAuthenticationService" >
<property name="configuration" ref="oauthConfiguration">
</property>
</bean>
具体类如下:
/**
* 因原逻辑不符合我们的业务需求 改写,
* 之前只有success fail,
* 现在有 success fail register
*/
public class MyOAuthAction extends AbstractAction{
private final Logger log = LoggerFactory.getLogger(OAuthAction.class);
@NotNull
private OAuthConfiguration configuration;
@NotNull
private CentralAuthenticationService centralAuthenticationService;
private String oauth10loginUrl = "/" + OAuthConstants.OAUTH10_LOGIN_URL;
@Override
protected Event doExecute(final RequestContext context) throws Exception {
final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
final HttpSession session = request.getSession();
// get provider type
final String providerType = request.getParameter(OAuthConstants.OAUTH_PROVIDER);
log.debug("providerType : {}", providerType);
// it's an authentication
if (StringUtils.isNotBlank(providerType)) {
// get provider
final OAuthProvider provider = OAuthUtils
.getProviderByType(this.configuration.getProviders(), providerType);
log.debug("provider : {}", provider);
// get credential
@SuppressWarnings("unchecked")
final OAuthCredential credential = provider.getCredential(new HttpUserSession(request),
request.getParameterMap());
log.debug("credential : {}", credential);
// retrieve parameters from web session
final Service service = (Service) session.getAttribute(OAuthConstants.SERVICE);
context.getFlowScope().put(OAuthConstants.SERVICE, service);
restoreRequestAttribute(request, session, OAuthConstants.THEME);
restoreRequestAttribute(request, session, OAuthConstants.LOCALE);
restoreRequestAttribute(request, session, OAuthConstants.METHOD);
// create credentials
final OAuthCredentials credentials = new OAuthCredentials(credential);
try {
WebUtils.putTicketGrantingTicketInRequestScope(context, this.centralAuthenticationService
.createTicketGrantingTicket(credentials));
Map<String, Object> attributes = credentials.getUserProfile().getAttributes();
//这个是判断该用户是否已存在我们的数据,存在返回 sucess 不存在我们构造客户端需要的参数重定向客户端并带上参数(这样有点安全问题,有更好的解决方案的可以联系QQ419499249)
if(attributes.get(Oauth2Info.EXIST)==null||!(Boolean)(attributes.get(Oauth2Info.EXIST))){
//存放的子系统的注册页面的信息
Object registerServiceUrl=session.getAttribute(ConstantsUtil.MY_REGISTER);
String url=(String)registerServiceUrl;
if(url==null || url.trim().isEmpty()||getUrlParam(attributes)==null){
return error();
}
//这里把重定向的url进行拼接并放进flow域中 属性名为oauthUniversalVerificationUrl
context.getFlowScope().put("oauthUniversalVerificationUrl", url.trim()+"?"+getUrlParam(attributes));
return result("register");
}
return success();
} catch (final TicketException e) {
//这里异常说明是在解析过程中出错 其实就是oauth2.0认证失败
return error();
}
} else {
// no authentication : go to login page
// save parameters in web session
final Service service = (Service) context.getFlowScope().get(OAuthConstants.SERVICE);
if (service != null) {
session.setAttribute(OAuthConstants.SERVICE, service);
}
//ConstantsUtil
//存放客户端给的注册链接供后面跳回客户端的注册页面使用
final String registerServiceUrl = request.getParameter(ConstantsUtil.MY_REGISTER);
if (StringUtils.trimToNull(registerServiceUrl)!=null) {
session.setAttribute(ConstantsUtil.MY_REGISTER, registerServiceUrl.trim());
}
saveRequestParameter(request, session, OAuthConstants.THEME);
saveRequestParameter(request, session, OAuthConstants.LOCALE);
saveRequestParameter(request, session, OAuthConstants.METHOD);
//这里是生成oauth2.0的各种登陆的链接的
// for all providers, generate authorization urls
for (final OAuthProvider provider : this.configuration.getProviders()) {
//这个就是页面通过${自己的provider类名Url } 这样页面就会自动生成登陆url
final String key = provider.getType() + "Url";
String authorizationUrl = null;
// for OAuth 1.0 protocol, delay request_token request by pointing to an intermediate url
if (provider instanceof BaseOAuth10Provider) {
authorizationUrl = OAuthUtils.addParameter(request.getContextPath() + this.oauth10loginUrl,
OAuthConstants.OAUTH_PROVIDER, provider.getType());
} else {
authorizationUrl = provider.getAuthorizationUrl(new HttpUserSession(session));
}
log.debug("{} -> {}", key, authorizationUrl);
context.getFlowScope().put(key, authorizationUrl);
}
}
return error();
}
/**
* Restore an attribute in web session as an attribute in request.
*
* @param request
* @param session
* @param name
*/
private void restoreRequestAttribute(final HttpServletRequest request, final HttpSession session, final String name) {
final String value = (String) session.getAttribute(name);
request.setAttribute(name, value);
}
/**
* Save a request parameter in the web session.
*
* @param request
* @param session
* @param name
*/
private void saveRequestParameter(final HttpServletRequest request, final HttpSession session, final String name) {
final String value = request.getParameter(name);
if (value != null) {
session.setAttribute(name, value);
}
}
public void setCentralAuthenticationService(final CentralAuthenticationService centralAuthenticationService) {
this.centralAuthenticationService = centralAuthenticationService;
}
public void setOauth10loginUrl(final String oauth10loginUrl) {
this.oauth10loginUrl = oauth10loginUrl;
}
public void setConfiguration(final OAuthConfiguration configuration) {
this.configuration = configuration;
for (final OAuthProvider provider : configuration.getProviders()) {
final BaseOAuthProvider baseProvider = (BaseOAuthProvider) provider;
// calculate new callback url by adding the OAuth provider type to the login url
baseProvider.setCallbackUrl(OAuthUtils.addParameter(configuration.getLoginUrl(),
OAuthConstants.OAUTH_PROVIDER, provider.getType()));
}
}
private String getUrlParam(Map<String,Object> map){
//需要传送的信息 name openid image type
StringBuffer param=new StringBuffer();
String name=(String)map.get(Oauth2Info.NAME);
if(StringUtils.trimToNull(name)!=null){
try {
name=URLEncoder.encode(name, "utf-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
String openid=(String)map.get(Oauth2Info.OPENID);
String image=(String)map.get(Oauth2Info.IMAGE);
Oauth20Type type=(Oauth20Type)map.get(Oauth2Info.TYPE);
param.append("openid=").append(openid).append("&")
.append("type=").append(type).append("&")
.append("name=").append(name).append("&")
.append("image=").append(image).append("&");
return param.substring(0, param.length()-1);
}
}
在你自己的cas的登陆页面加入超链接
<a href="${SinaWeiboProviderUrl }">微博登陆</a>
ConstantsUtil类如下:
/**
* 常量
*/
public class ConstantsUtil {
public final static String MY_REGISTER = "registerUrl";
}
②需要注入一个configuration我们在deployerConfigContext.xml里配置一个
<!-- 新浪微博配置 -->
<bean id="oauthConfiguration" class="org.jasig.cas.support.oauth.OAuthConfiguration">
<property name="providers">
<list>
<ref bean="sinaWeibo" />
</list>
</property>
<property name="loginUrl" value="${cas.securityContext.casProcessingFilterEntryPoint.loginUrl}"></property>
</bean>
③此配置需要一个providers集合 我们目前只配置了一个新浪的,在applicationContext.xml添加如下配置
<!-- 新浪微博 -->
<bean id="sinaWeibo" class="包名.SinaWeiboProvider">
<property name="key" value="key" />
<property name="secret" value="secret" />
<!-- 下面的callbackUrl 在测试中无效,会自动生成 回跳后的cas地址取自配置 cas.properties里面的server.name -->
<property name="callbackUrl" value="https://api.weibo.com/oauth2/default.html" />
</bean>
此类的具体代码如下:
添加新浪api类
/**
*新浪微博的api
*/
public class SinaWeiboApi20 extends DefaultApi20
{
private static final String AUTHORIZE_URL = "https://api.weibo.com/oauth2/authorize?client_id=%s&redirect_uri=%s&response_type=code";
private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s";
@Override
public Verb getAccessTokenVerb()
{
return Verb.POST;
}
@Override
public AccessTokenExtractor getAccessTokenExtractor()
{
return new JsonTokenExtractor();
}
@Override
public String getAccessTokenEndpoint()
{
return "https://api.weibo.com/oauth2/access_token?grant_type=authorization_code";
}
@Override
public String getAuthorizationUrl(OAuthConfig config)
{
// Append scope if present
if (config.hasScope())
{
return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback()), OAuthEncoder.encode(config.getScope()));
}
else
{
return String.format(AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback()));
}
}
}
/**
* 新浪微博Provider
*/
public class SinaWeiboProvider extends MyBaseOauth20Provider {
private String userInfoUrl="https://api.weibo.com/2/users/show.json";
@Override
protected String getProfileUrl() {
return "https://api.weibo.com/2/account/get_uid.json";
}
@Override
public String getOpenId(String body) {
String jsonBody="["+body+"]";
JSONArray jsonArray = JSONArray.fromObject(jsonBody);
JSONObject object = (JSONObject)jsonArray.get(0);
return object.getString("uid");
}
@Override
public Oauth20Type getOauth20Type() {
return Oauth20Type.SINA;
}
@Override
public String getOpenName(String uid, Token accessToken,JSONArray jsonArray) {
String openName=null;
String body=null;
if(jsonArray==null){
String newUserInfoUrl=userInfoUrl+"?uid="+uid+"&access_token="+accessToken.getToken();
try {
body = "["+CommonsUtils.getJson(newUserInfoUrl)+"]";
jsonArray=JSONArray.fromObject(body);
} catch (Exception e) {
e.printStackTrace();
}
}
try {
JSONObject object = (JSONObject)jsonArray.get(0);
openName=object.getString("name");
} catch (Exception e) {
e.printStackTrace();
}
return openName;
}
@Override
public String getOpenImageUrl(String uid, Token accessToken,JSONArray jsonArray) {
String openImageUrl=null;
String body=null;
if(jsonArray==null){
String newUserInfoUrl=userInfoUrl+"?uid="+uid+"&access_token="+accessToken.getToken();
try {
body = "["+CommonsUtils.getJson(newUserInfoUrl)+"]";
jsonArray=JSONArray.fromObject(body);
} catch (Exception e) {
e.printStackTrace();
}
}
try {
JSONObject object = (JSONObject)jsonArray.get(0);
openImageUrl=object.getString("profile_image_url");
} catch (Exception e) {
e.printStackTrace();
}
return openImageUrl;
}
}
父类代码如下:
/**
* 将 oauth2.0 验证的相同的地方抽取出来
*/
public abstract class MyBaseOauth20Provider extends BaseOAuth20Provider{
String scope=null;
@Override
protected void internalInit() {
if (scope != null) {
service = new ServiceBuilder().provider(SinaWeiboApi20.class).apiKey(key)
.apiSecret(secret).callback(callbackUrl).scope(scope).build();
} else {
service = new ServiceBuilder().provider(SinaWeiboApi20.class).apiKey(key)
.apiSecret(secret).callback(callbackUrl).build();
}
}
@Override
protected UserProfile extractUserProfile(String body) {
UserProfile userProfile = new UserProfile();
String openId = getOpenId(body);
if(StringUtils.trimToNull(openId)!=null){
userProfile.setId(openId);
//这里添加属性是为了后面的拼接参数
userProfile.addAttribute(Oauth2Info.OPENID, openId);
}
//这里添加属性是为了后面的拼接参数 和 数据库查询的时候区分类别
userProfile.addAttribute(Oauth2Info.TYPE, getOauth20Type());
return userProfile;
}
@Override
protected UserProfile getUserProfile(final Token accessToken) {
final String body = sendRequestForData(accessToken, getProfileUrl());
if (body == null) {
return null;
}
final UserProfile profile = extractUserProfile(body);
addAccessTokenToProfile(profile, accessToken);
//这个是为了当一个请求可以获取所有需要的数据时避免二次访问
JSONArray jsonArray=null;
//这里添加属性是为了后面的拼接参数
profile.addAttribute(Oauth2Info.NAME, getOpenName(profile.getId(),accessToken,jsonArray));
profile.addAttribute(Oauth2Info.IMAGE, getOpenImageUrl(profile.getId(),accessToken,jsonArray));
return profile;
}
/**
* 解析数据获取aouth2.0 对应系统的用户名称
* @param body
* @return
*/
public abstract String getOpenName(String uid, Token accessToken,JSONArray jsonArray);
/**
* 解析数据获取aouth2.0 对应系统的图片的网络路径
* @param body
* @return
*/
public abstract String getOpenImageUrl(String uid, Token accessToken,JSONArray jsonArray);
/**
* 解析数据获取openId
* @param body
* @return
*/
public abstract String getOpenId(String body);
/**
* 获取oauth2.0的类型
* @return
*/
public abstract Oauth20Type getOauth20Type();
/**
* 克隆的时候用到 需要的话自己实现
*/
protected BaseOAuthProvider newProvider() {
// TODO Auto-generated method stub
return null;
}
}
CommonsUtils类如下:
public class CommonsUtils {
/**
* 通过url链接获取json数据
*/
public static String getJson(String url) throws Exception {
String data = "";// 访问返回结果
BufferedReader read = null;// 读取访问结果
try {
// 创建url
URL realurl = new URL(url);
// 打开连接
URLConnection connection = realurl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
// 建立连接
connection.connect();
// 获取所有响应头字段
Map<String, List<String>> map = connection.getHeaderFields();
// 遍历所有的响应头字段,获取到cookies等
// 定义 BufferedReader输入流来读取URL的响应
read = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;// 循环读取
while ((line = read.readLine()) != null) {
data += line;
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
} finally {
if (read != null) {// 关闭流
try {
read.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return data;
}
}
5.添加我们自己的数据库校验,已存在,登陆成功,不存在,重定向注册界面
修改deployerConfigContext.xml
①在name="credentialsToPrincipalResolvers"的list节点添加
<!-- 新浪微博 -->
<bean class="org.jasig.cas.support.oauth.authentication.principal.OAuthCredentialsToPrincipalResolver" >
<property name="attributeRepository" ref="oauthAttributeRepository" />
</bean>
②在deployerConfigContext.xml添加如下bean
<!-- 新浪微博 -->
<bean id="oauthAttributeRepository"
class="org.jasig.services.persondir.support.StubPersonAttributeDao">
</bean>
③在name="authenticationHandlers"的list节点添加
<!-- 新浪微博 -->
<bean class="包名.Oauth20QueryDatabaseAuthenticationHandler">
<property name="configuration" ref="oauthConfiguration"/>
<property name="dataSource" ref="dataSource" />
<property name="passwordEncoder" ref="passwordEncoder"/>
<property name="querySql" value="select 字段 from 表名 o where 1=1 "/>
</bean>
Oauth20QueryDatabaseAuthenticationHandler类如下:
/**
* 通过继承 改写此类 从数据库获取用户信息,
* 获取成功说明此用户已经在本系统了,获取失败说明此用户未关联本系统的账号
*/
public class Oauth20QueryDatabaseAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler{
@NotNull
private OAuthConfiguration configuration;
@NotNull
private JdbcTemplate jdbcTemplate;
@NotNull
private DataSource dataSource;
@NotNull
private String querySql;
@NotNull
private PasswordEncoder passwordEncoder = new PlainTextPasswordEncoder();
@Override
public boolean supports(final Credentials credentials) {
return credentials != null && (OAuthCredentials.class.isAssignableFrom(credentials.getClass()));
}
@Override
protected boolean doAuthentication(final Credentials credentials) throws AuthenticationException {
final OAuthCredentials oauthCredentials = (OAuthCredentials) credentials;
log.debug("credential : {}", oauthCredentials);
final String providerType = oauthCredentials.getCredential().getProviderType();
log.debug("providerType : {}", providerType);
// get provider
final OAuthProvider provider = OAuthUtils.getProviderByType(this.configuration.getProviders(), providerType);
log.debug("provider : {}", provider);
// get user profile
final UserProfile userProfile = provider.getUserProfile(oauthCredentials.getCredential());
log.debug("userProfile : {}", userProfile);
if (userProfile != null && StringUtils.isNotBlank(userProfile.getId())) {
oauthCredentials.setUserProfile(userProfile);
String sql=querySql+" and o.open_id = ? and o.system_type = '"+userProfile.getAttributes().get(Oauth2Info.TYPE)+"'";
//通过 openid 和 类型查询看数据库是否有数据
String memberId=null;
try {
memberId = getJdbcTemplate().queryForObject(sql, String.class, userProfile.getId());
} catch (DataAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(!StringUtils.isEmpty(memberId)){
//有则说明该用户已经在本系统存在
userProfile.addAttribute(Oauth2Info.EXIST, true);
userProfile.addAttribute(Oauth2Info.ID, memberId);
}
return true;
} else {
return false;
}
}
public void setConfiguration(final OAuthConfiguration configuration) {
this.configuration = configuration;
}
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.dataSource = dataSource;
}
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
public void setQuerySql(String querySql) {
this.querySql = querySql;
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
}
④在id="authenticationManager" 节点下添加此配置,作用是认证成功后客户端可以拿到认证后的数据
<!-- 新浪微博 -->
<property name="authenticationMetaDataPopulators">
<list>
<bean class="org.jasig.cas.support.oauth.authentication.OAuthAuthenticationMetaDataPopulator" />
</list>
</property>
0 0
- cas3.5.3添加微博登陆(操作笔记)
- 微信登陆笔记
- [RK3288][Android6.0] 调试笔记 --- Su添加密码登陆功能
- 有道笔记有网却不能进行微博登陆
- 登陆 笔记
- cas3.5.2客户端配置
- springboot集成cas3.5.2
- 登陆、添加、查询、删除
- 添加登陆Dlg
- ubuntu 登陆界面添加root登陆
- 新浪微博登陆
- 微博模拟登陆
- 仿新浪微博2014之登陆界面一(sqlite操作)
- SQL登陆用户操作
- mac首次登陆操作
- form实现登陆操作
- ThinkPHP-登陆操作
- SourceTree 免登陆操作
- 自定义viewgroup(4)--快速滑动
- 第十六周点结构体
- 略谈float
- python3中json.dumps()的问题
- 从零开始的Python入门教程0
- cas3.5.3添加微博登陆(操作笔记)
- 《挖地雷MineSweeper》源码
- C#中Dictionary用法
- idea报错:Error during artifact deployment. See server log for details.
- 自定义viewgroup(5)--可滚动布局,GestureDetector手势监听
- Android Activity生命周期
- jps命令使用
- 第十五周-OJ-F将字符串格式化后输出(串)
- 双截棍(程序员版)