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> 
0 0
原创粉丝点击