如何登陆路由器ATT

SSO:单点登录
本文采用两个方法实现简单单点登录
1.方法1是cookie+filter实现
2.方法2是创建一个CAS服务器实现(底层依旧是cookie实现,所以两个不同浏览器之前也是无法单点登录的)
本文图片没有显示,请自行下载附件的SSO.doc观看
百度云盘 资料地址:/s/1bnXrsS3
AUTHOR:zzm
1、使用Cookie解决单点登录
&&&&&& 技术点:
&&&&&& 1、设置Cookie的路径为setPath("/") .即Tomcat的目录下都有效
2、设置Cookie的域setDomain(".");即,或是有效。即跨域。
3、设置Cookie的时间。即使用户不选择在几天内自动登录,也应该保存Cookie以保存在当前浏览器没有关闭的情况下有效。
4、使用Filter自动登录。
实现步骤:
1、首先要准备出几个虚拟主机并配置hosts文件,即本机DNS。
&&&&&& 配置虚拟主机,主要通过修改tomcat_home/conf/server.xml文件完成:
增加几个Host节点,通过Cookie实现自动登录,必须配置的虚拟主页满足,即主域名必须保持一致。
2、先在bbs(或是mail)虚拟目录下,开发一个可以自动登录的程序,使用Filter:
1、登录的主页如下:
&%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%&
&%@ taglib uri="/jsp/jstl/core" prefix="c"%&
&&& &p&在同一台服务器上,多个站点自动登录....&&:&%=session.getId()%&&/p&
&&& &c:if test="${empty sessionScope.user}"&
&&& &&& &form name="f" method="post" action="&c:url value='/login'/&"&
&&&&&&&&&&&&& Name:&input type="text" name="name"/&&br/&
&&&&&&&&&&&&& Pwd:&input type="text" name="pwd"/&&br/&
&&&&&&&&&&&&& &input type="checkbox" name="chk" value="7"&一周内自动登录&br/&
&&&&&&&&&&&&& &input type="submit" value="登录"/&&& &&&&&
&&& &&& &/form&
&&& &/c:if&
&&& &c:if test="${not empty sessionScope.user}"&
&&& &&& 欢迎你:${user}。&a href="&c:url value='/loginout'/&"&安全退出&/a&
&&& &/c:if&
&&& 相关站点:(只要在一边登录成功,即可以自动登录到另一个程序)&br/&
&&& &a href=":7777"&&/a&&br/&
&&& &a href=":7777"&&/a&&br/&
2、登录的Servlet程序如下:
&* 用户登录
public class LoginServlet extends HttpServlet{
&&& public void doGet(HttpServletRequest req, HttpServletResponse resp)
&&&&&&&&&& throws ServletException, IOException {
&&&&&& doPost(req, resp);
&&& public void doPost(HttpServletRequest req, HttpServletResponse resp)
&&&&&&&&&& throws ServletException, IOException {
&&&&&& String nm = req.getParameter("name");
&&&&&& String pwd = req.getParameter("pwd");
&&&&&& String chk = req.getParameter("chk");& //是否选中了7天自动登录
&&&&&& String forward = "/index.jsp";
&&&&&& if(nm!=null && !nm.trim().equals("") && nm.startsWith("it")//用户名是it开始,且密码是pwd开始的可以登录
&&&&&&&&&&&&& && pwd !=null && !pwd.trim().equals("") &&
&&&&&&&&&&&&& pwd.startsWith("pwd")){
&&&&&&&&&& System.err.println("登录成功。。。。。");
&&&&&&&&&& forward = "/jsps/welcome.jsp";
&&&&&&&&&& //无论如何,都要设置cookie,如果没有选择自动登录,则只在当前页面的跳转时有效,否则设置有效期间为7天。
&&&&&&&&&& Cookie cookie = new Cookie("autologin",nm+"@"+pwd);
&&&&&&&&&& cookie.setPath("/"); &&&&&&&&&& //如果路径为/则为整个tomcat目录有用
&&&&&&&&&& cookie.setDomain(".");&& //设置对所有*.为后缀的域名效
&&&&&&&&&& if(chk!=null){
&&&&&&&&&&&&& int time = 1*60*60*24*7; //1秒*60=1分*60分=1小时*24=1天*7=7天
&&&&&&&&&&&&& cookie.setMaxAge(time);
&&&&&&&&&& }
&&&&&&&&&& resp.addCookie(cookie);
&&&&&&&&&& req.getSession().setAttribute("user", nm);
&&&&&& }else{
&&&&&&&&&& System.err.println("登录不成功。。。。。。");
&&&&&& req.getRequestDispatcher(forward).forward(req, resp);
3、自动登录的Filter程序如下:
&* 自动登录
public class AutoLogin implements Filter {
&&& public void destroy() {}
&&& public void doFilter(ServletRequest req, ServletResponse resp,
&&&&&&&&&& FilterChain chain) throws IOException, ServletException {
&&& &&& System.err.println("开始自动登录验证.....");//此类中应该对登录的servlet直接放行。根据判断url决定。
&&&&&& HttpServletRequest requ = (HttpServletRequest)
&&&&&& HttpSession s = requ.getSession();
&&&&&& if (s.getAttribute("user") != null) {//如果用户已经登录则直接放行
&&&&&&&&&& System.err.println("用户已经登录,没有必须要再做自动登录。。。。");
&&&&&& } else {
&&&&&&&&&& Cookie[] cookies = requ.getCookies();
&&&&&&&&&& if (cookies != null) {
&&&&&&&&&&&&& for (Cookie ck : cookies) {
&&&&&&&&&&&&&&&&& if (ck.getName().equals("autologin")) {// 是否是自动登录。。。。
&&&&&&&&&&&&&&&&&&&& System.err.println("自动登录成功。。。。。");
&&&&&&&&&&&&&&&&&&&& String val = ck.getValue();
&&&&&& &&&&&&&&&&&&& String[] vals = val.split("@");
&&&&&&&&&&&&&&&&&&&& s.setAttribute("user", vals[0]);
&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&& }
&&&&&&&&&& }
&&&&&& chain.doFilter(req, resp);
&&& public void init(FilterConfig filterConfig) throws ServletException {}
4、正常退出的Servlet如下
&* 安全退出删除Cookie
public class LoginOutServlet extends HttpServlet {
&&& public void doGet(HttpServletRequest req, HttpServletResponse resp)
&&&&&&&&&& throws ServletException, IOException {
&&&&&& HttpSession s = req.getSession();&&&&& //获取Session
&&&&&& Cookie cookie = new Cookie("autologin","");//必须声明一个完全相同名称的Cookie
&&&&&& cookie.setPath("/");//路径也要完全相同
&&&&&& cookie.setDomain(".");//域也要完全相同
&&&&&& cookie.setMaxAge(0);//设置时间为0,以直接删除Cookie
&&&&&& resp.addCookie(cookie);
&&&&&& s.removeAttribute("user");
&&&&&& System.err.println("安全退出。。。。。");
&&&&&& resp.sendRedirect(req.getContextPath()+"/index.jsp");
5、关于自动登录的一些思考
为了让单点登录,变成可配置的功能,可以将保存Cookie的代码,放到自动登录的Filter中实现。
如果配置自动登录的Filter则同时实现闪单点登录,否则不加实现。
2、SSO示例程序配置
在CAS的主页上,可以看到CAS服务器,和客户端配置的完整过程,根据提示,完全可以配置成功服务器和客户端。同时,在CAS上也可以找到服务器端的程序和客户端的程序,都是已经配置好的,对于初步学习来说,完全可以直接取来配置测试。以下就是直接使用CAS官方提供的示例服务器和示例客户端配置一个单点登录的示例:
Jasig主页:
第一步:先下载cas的服务器端支持包:cas-server3.xx.zip
第二步:下载cas有客户端支持包。Cas-client.zip
第三步:下载客户端的示例程序,这个不太好找。需要有点耐心
以下是下载的超连接:
第四步:解压cas-server.zip如下图,并从中找到服务器端war文件
服务器端程序一般不用我们完成,但需要做一点小小的修改,cas的服务器端程序由spring+Spring web flow+cas写成。全部使用spring配置文件。它就是一个用户登录验证、发售票据的中心。相当于Ticket Office(售票处)。
第五步:分别建立三个虚拟主机。
&&&&&& 一个是服务器主机,和两个客户端虚拟主机。
&&&&&& 1、修改etc/hosts文件:
&&&&&& 2、修改tomcat/conf/server.xml配置文件,增加虚拟主机目录,同时将服务器的端口修改成80。
&&&&&& 3、在tomcat的根目录下,分别建立三个目录,即server、bbs、news。
&&&&&&&&&&&&& 在三个目录下,分别都建立一个ROOT(ROOT是tomcat的主默认主页目录)文件夹。
&&&&&&&&&&&&& 将cas-server.xx.war解压后散放到/tomcat/server/ROOT目录下。如下图:
&&&&&&&&&&&&& 注意目录结构,是散放到ROOT的目录下。
第六步:先测试服务器是否可以正常使用
 启动tomcat,在地址栏输入:
请先保证在单个服务器上登录可以登录成功。如果不能登录成功,请重复前面的配置。
第七步:配置两个客户端
&&&&&& 将下载的文件分别解压到tomcat/bbs/ROOT目录下和tomcat/news/ROOT目录下。注意是散放到ROOT目录下。
由于在mywebapp.war中并没有放置依赖的jar文件,所以,还需要我们添加它所依赖的jar文件,为此我为大家准备了已经放放置好的
mywebapp.war文件。
放置好的目录结构如下:
(bbs程序同,不再上图)
WEB-INF/lib目录下的包如下:
这两个包,在cas-client.rar文件中都可以找到。
此处,你可以启动一个tomcat,如果启动成功,则进入下一步。
第八步:修改客户端的配置文件
&&&&&& 当使用登录客户端受保护的资源时,如果发现还没有登录,则会重定向到服务器(售票处)请求登录验证,登录成功后即会获取一张票据,服务器会携带这张票据再重定向到客户端页面。
修改客户端的web.xml配置文件,让它在登录时,知道去哪台服务器:
注意将里面的https全部修改成http。
修改的部分主要分为两块:
1:修改登录重定向过虑器,它用于保护受保护的资源,如果发面用户在访问受保护的资源时,用户还没有登录,则会重定向到服务器,要求用户登录:
2、修改验证过虑器,它的主要作用是接收服务器发送的Ticket,进行验证
其他没有说明的部分,请不要修改。
请参考上面的实现配置另一个项目的web.xml文件。
第九步:测试登录
&&&&&& 目前还不能实现单点登录。但可以对任意的一个客户端进行登录验证。
1、& 在地址栏输入
点击访问受保护的页面:
将重定向到服务器请求登录:
登录成功后即重定回原请求页面:
第十步:配置可以单点登录
&&&&&& Cas服务器都是用spring配置文件配置而成。且使用了cookie技术。在ticketGrantingTicketCookieGenerator.xml文件中,保存了cookie的生成方式及有效时间。
注意,这是在server服务器上的spring配置文件。
打开此文件,修改成以下内容:
说明:false是指支持http协议登录。默认为true,支持https登录。
&&&&&&  3600中cookie保存在本地的时间,默认为-1即浏览器缓存。
   cookiePath是cookie的path设置。
第十一步:单点登录测试
&&&&&& 修改了上面文件后,即可测试是否可以从一个点的登录,即可以访问两个网站时都显示先登录的姓名:
&&&&&& 先输入
&&&&&& 登录
登录成功:
在地址栏直接输入:
可以看到,显示的是news用户名,即之前在上登录的用户名,即实现单点登录。
好了,以上步骤,同学们先自己完成,如果可以配置成功,再进入下一步。
3、CAS单点登录流程图
4、在Eclipse环境中开发SSO
&&&&&& 为了文件书写代码,我们需要将cas-server导入到eclipse环境中:
1、建立一个新的web项目:
2、将cas-server.war文件中的文件放到Eclipse web项目的相关目录下:
注意:src下的文件需要到cas-server/WEB-INF/classes下单独copy.
3、重新设置虚拟目录
将原来的虚拟主机目录删除。配置新的虚拟目录,并通过&Context/&形式指定Eclipse下的项目为项目的根目录,如下:
Host的name保先原地址不变,这样就没有必要修改hosts文件了。在tomcat的根目录下,创建一个casServer目录,里面什么也不用放即可。
通过&Context path=&/&/&指定根目录为Eclipse中的项目。
测试是否可以访问。
4、建立客户端项目-两个
项目中使用了jstl.jar包,应该放到WEB-INF/lib目录下。
5、配置两个项目的虚拟目录
&&&&&& 将原来配置的两个虚拟主机目录删除,然后使用&Context/&配置新的项目。如下:
6、准备cas-client.jar包
7、声明受保护的资源并在web.xml中配置
&&&&&& 此处可以完全参考原有客户端项目的配置。(略)
运行测试进入下一步。
5、在客户端面获取用户的基本信息-name
&&&&&& 1、Cas在登录成功后会通过socket向客户端传递用户信息。一般情况下就是用户名而已。
&&&&&& 2、cas客户端的request对象是经过包装的org.jasig.cas.client.util.HttpServletRequestWrapperFilter$CasHttpServletRequestWrapper。
&&&&&& 3、在用户成功后,客户端的过虑器,会将用户的信息封装成java.security.Principal的子封装到Assertion后放到Session中。
&&&&&& 关于Assertion对象的结构图,请见后面的部分。
以下是在客户端的页面上如何获取用户名信息的多种方式:
那么,如何才可以返回更多用户的信息呢。如返回用户和用户名和id?后面的章节将会讲到。
6、修改服务器的登录验证规则
1、使用配置的用户名和密码
目前服务器的验证方式为用户和密码相同即为登录成功。这并不符合我们的业务需求,大多数应用都是通过查询数据库获取用户名和密码的。那我们又如何设置从数据库获取用户名和密码呢?
&&&&&& 为了便于理解,我先将服务器的登录方式修改为配置的。而后再修改成通过数据库进行验证的。
&&&&&& 修改配置文件:/WEB-INF/deployerConfigContext.xml。& -
&&& 在这个配置文件中,保存了多个用户认证登录的验证方式,只要有一种验证通过即可以登录成功。
&&&&&& 在&property name=&"authenticationHandlers"&...属性内部,通过配置若干的AuthenticationHandler的子类,可以改变登录认证方式。也可以增加认证方式,只要在用户登录时,有一种登录方式是可行的,即可以登录成功。
&&& 认证类的继承关系如下:
&&& 默认已经配置了最后一个类即SimpleTestUsernamePasswordAuthenticationHandler。此类验证用户名和密码是否一致。
&&& Cas为我们提供了可以直接配置用户名和密码类:即org.jasig.cas.adaptors.generic.AcceptUsersAuthenticationHandler。此类并没有包含到默认的服务器代码中,需要到cas-server/models/中查找名为:cas-server-support-generic-3.4.11.jar的jar文件,并添加到WEB-INF/lib目录下。
如下图所示,正是名为generic的jar包。(后面如果需要数据库连接的,还需要jdbc的jar包)
将此jar包放到lib目录下后,类的层次关系如下:
此类接收一个map当作用户名和密码的集合列表:
在/WEB-INF/deployerConfigContext.xml配置文件中的&property name=&"authenticationHandlers"&元素中,删除原来的用户名与密码相同的认证,即:
&!-- &bean
&&&&&&&&&&&&& class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" /& --&
然后在相同的位置配置:AcceptUsersAuthenticationHandler类:
配置如下:
登录测试:
直接在配置文件中配置用户名和密码:可选的加密
&!-- 通用的认证管理器,通过一个文件或是一个map配置用户名和密码 --&
&&&&&&&&&&&&& &bean class="org.jasig.cas.adaptors.generic.AcceptUsersAuthenticationHandler"&
&&&&&&&&&&&&&&&&& &property name="users"&
&&&&&&&&&&&&&&&&&&&& &map&
&&&&&&&&&&&&&&&&&&&&&&&& &!-- 1234 MD5:4321 --&
&&&&&&&&&&&&&&&&&&&&&&&& &entry key="tom" value="81dc9bdb52d04dc20036dbd"/&
&&&&&&&&&&&&&&&&&&&&&&&& &!-- 4321: md5 :& --&
&&&&&&&&&&&&&&&&&&&&&&&& &entry key="Jack" value="d93591bdfee2fca"&&/entry&
&&&&&&&&&&&&&&&&&&&& &/map&
&&&&&&&&&&&&&&&&& &/property&
&&&&&&&&&&&&&&&&& &!-- 可选的使用md5进行加密 --&
&&&&&&&&&&&&&&&&& &property name="passwordEncoder"&
&&&&&&&&&&&&&&&&&&&& &bean class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder"&
&&&&&&&&&&&&&&&&&&&&&&&& &constructor-arg value="MD5"&&/constructor-arg&
&&&&&&&&&&&&&&&&&&&&&&&& &property name="characterEncoding" value="UTF-8"&&/property&
&&&&&&&&&&&&&&&&&&&& &/bean&
&&&&&&&&&&&&&&&&& &/property&
&&&&&&&&&&&&& &/bean&&&&&&&
2、自己书写验证规则-颠倒的用户名和密码
&&&&&& 1、所有的认证类都是AuthenticationHandler的子类,于是我们可以自己开发基于任何规则的验证。
&&& 2、基于用户名和密码的认证,则应该继承AbstractUsernamePasswordAuthenticationHandler。
&&& 3、代码如下:
package cn.itcast.
import org.jasig.cas.authentication.handler.AuthenticationE
import org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationH
import org.jasig.cas.authentication.principal.UsernamePasswordC
&* 颠倒的用户名和密码即可以登录
&* @author 传智播客
&* credentials:凭据
public class MyReverseUsernamePasswordHandler
&&&&&&&&&& extends AbstractUsernamePasswordAuthenticationHandler {
&&& protected boolean authenticateUsernamePasswordInternal(
&&&&&&&&&& UsernamePasswordCredentials credentials)
&&&&&&&&&& throws AuthenticationException {
&&&&&& String name = credentials.getUsername();
&&&&&& String pwd& = credentials.getPassword();
&&&&&& if(name.equals(new StringBuffer(pwd).reverse().toString())){
&&&&&&&&&& System.err.println("登录成功.....");
&&&&&&&&&& return true;
&&&&&& System.err.println("登录不成功。。。");
&&&&&& return false;
&&& 4、配置如下:
&&& 将原有的配置用户名和密码的方式删除,然后只配置上面的MyReverseUsernamePasswordHandler类:
7、配置使用JDBC的登录&,认证句柄: AuthenticationHandler
&&&&&& 这儿需要添加新的包:,需要同时添加mysql-connection.jar文件,以连接数据库。
&&&&&& 添加此包以后,AuthenticationHandler的层次结构为:
通过配置可以,修改CAS的登录认证方式,默认的登录方式为用户名与密码一致即可以登录。
&&&&&& 在&property name=&"authenticationHandlers"&...属性内部,通过配置若干的AuthenticationHandler的子类,可以改变登录认证方式。也可以增加认证方式,只要在用户登录时,有一种登录方式是可行的,即可以登录成功。
&&& 所有的配置方式,在CAS的官方网站上均有详细的说明。
&&& 修改配置文件:/WEB-INF/deployerConfigContext.xml。
&&& 在这个配置文件中,保存了多个用户认证登录的验证方式,只要有一种验证通过即可以登录成功。
&&& 以下是在配置文件中增加用户名和密码的登录的方式,其中设置了MD5对密码进行加密。默认的加密方式为PlainTextPasswordEncoder.即不加密。
1、使用数据库指定表名,字段名的登录认证方式
&&&&&& A:创建数据库:
create database sso character set UTF8;
create table users(
&& id varchar(32),
&& name varchar(30),
&& pwd varchar(32)
insert into users values('U001','Jack','1234');
insert into users values('U002','Rose','4321');
B:在/WEB-INF/deployerConfigContext.xml文件的最下面,创建数据连接:
&bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"&
&&& &property name="driverClassName" value="com.mysql.jdbc.Driver"/&
&&& &property name="url" value="jdbc:mysql:///sso?characterEncoding=UTF-8"/&
&&& &property name="username" value="root"/&
&&& &property name="password" value="1234"/&
C:使用dataSource数据源,因为后面的查询需要一个数据源的支持:
&!-- 使用数据库验证,且数据库的密码是经过md5加密的 --&
&&&&&&&&&&&&& &bean class="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler"&
&&&&&&&&&&&&&&&&& &property name="dataSource" ref="dataSource"/&
&&&&&&&&&&&&&&&&& &property name="tableUsers" value="users"/&
&&&&&&&&&&&&&&&&& &property name="fieldUser" value="name"/&
&&&&&&&&&&&&&&&&& &property name="fieldPassword" value="pwd"/&
&&&&&&&&&&&&&&&&& &property name="passwordEncoder"& &!--可选的加密--&
&&&&&&&&&&&&&&&&&&&& &bean class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder"&
&&&&&&&&&&&&&&&&&&&&&&&& &constructor-arg value="MD5"/&
&&&&&&&&&&&&&&&&&&&&&&&& &property name="characterEncoding" value="UTF-8"&&/property&
&&&&&&&&&&&&&&&&&&&& &/bean&
&&&&&&&&&&&&&&&&& &/property&
&&&&&&&&&& &/bean&
MySql的表结构如下:
2、使用查询sql验证用户登录的方式
&!-- 使用sql语句 --&
&&&&&&&&&&&&& &bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"&
&&&&&&&&&&&&&&&&& &property name="dataSource" ref="dataSource"/&
&&&&&&&&&&&&&&&&& &property name="sql" value="select pwd from users where name=?"/&
&&&&&&&&&&&&&&&&& &property name="passwordEncoder"&
&&&&&&&&&&&&&&&&&&&& &bean class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder"&
&&&&&&&&&&&&&&&&&&&&&&&& &constructor-arg value="MD5"/&
&&&&&&&&&&&&&&&&& &&&&&& &property name="characterEncoding" value="UTF-8"&&/property&
&&&&&&&&&&&&&&&&&&&& &/bean&
&&&&&&&&&&&&&&&&& &/property&
&&&&&&&&&&&&& &/bean&
表结构如下:
其实,上面的MD5验证,完全可以使用同一个。
上面的程序非常简单,完全可以通过查看源代码和继承结构图来了解认证和登录方式。且不需要书写任何Java代码。
8、返回用户的更多信息
1、如返回用户名和用户ID
配置返回用户的ID而不是用户名,用户凭据:Credentials和用户对象(被代理人)&:Principal。
&&&&&& &&&&&& Credentials :用于定义用户以什么样的凭据登录,普通的凭据为用户名和密码凭据。
&&&&&&&&&&&&& Principal&:用户登录成功以后,使用Credentials转换成Principal。Principal中包含了用户的用户名(默认)及一些其他属性信息。
Credentials及Pincipal关系的图示,及Principal内部结构图示:
Principal (用户对象,被代理人)
&&&&&&&&&&&&&&&&&&&& String getId() & 返回用户的唯一标识
&&&&&&&&&&&&&   Map& getAttributes() 用户的其他有用信息
Principal的源代码如下:(注意此Principal不是java.security.Principal类。而是由cas自己定义的一个接口)
package org.jasig.cas.authentication.
import java.io.S
import java.util.M
public interface Principal extends Serializable {
&&& String getId();
&&& Map&String, Object& getAttributes();
为了可以返回用户的ID,我们可以修改deployerConfigContext.xml文件中的&property name=&credentialsToPrincipalResolvers&&&中的&list/&元素中的Bean。
此时,为了可以返回用户的ID,我们必须要手工创建一个CredentialsToPrincipalResolver的子类。
具体代码如下:
package cn.itcast.
import javax.sql.DataS
import org.jasig.cas.authentication.principal.C
import org.jasig.cas.authentication.principal.CredentialsToPrincipalR
import org.jasig.cas.authentication.principal.P
import org.jasig.cas.authentication.principal.SimpleP
import org.jasig.cas.authentication.principal.UsernamePasswordC
import org.springframework.jdbc.core.JdbcT
public class MyCredentialsToPrincipalResolver implements CredentialsToPrincipalResolver {
&&& private DataSource dataS//查询数据库用
&&& @Override
&&& public Principal resolvePrincipal(Credentials credentials) {
&&&&&& UsernamePasswordCredentials up = //强制类型转换
&&&&&&&&&&&&& (UsernamePasswordCredentials)
&&&&&& String name = up.getUsername();
&&&&&& String pwd& = up.getPassword();
&&&&&& String sql = "select id from users2 where u2_name=? and u2_pwd=?"; //查询id - 一般只根据用户查询即可
&&&&&& String id& = null;
&&&&&& try{
&&&&&&&&&& id=new JdbcTemplate(getDataSource()).queryForObject(sql, String.class, name,pwd);
&&&&&&&&&& if(id!=null){
&&&&&&&&&&&&& Principal p = new SimplePrincipal(id);//封装成包含id的Principal对象
&&&&&&&&&&&&& return
&&&&&&&&&& }
&&&&&& }catch(Exception e){
&&&&&&&&&& e.printStackTrace();
&&&&&& return null;
&&& @Override
&&& public boolean supports(Credentials credentials) {
&&&&&& boolean boo =& //判断是否是用户和密码凭据
&&&&&&&&&& &&& UsernamePasswordCredentials.class.isAssignableFrom(credentials.getClass());
&&&&&& return
&&& public DataSource getDataSource() {
&&&&&& return dataS
&&& public void setDataSource(DataSource dataSource) {
&&&&&& this.dataSource = dataS
(在使用了ID的查询以后,应该只使用数据库进行验证。)
在配置文件中配置如下:
&&&&&& 配置文件为:/WEB-INF/deployerConfigContext.xml
&property name="credentialsToPrincipalResolvers"&
&&&&&&&&&& &list&
&&&&&&&&&&&&& &bean class="cn.itcast.pubs.MyCredentialsToPrincipalResolver"&
&&&&&&&&&&&&&&&&& &property name="dataSource" ref="dataSource"&&/property&
&&&&&&&&&&&&& &/bean&
&&&&&&&&&&&&& &!--
&&&&&&&&&&&&& &bean
&&&&&&&&&&&&& class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" /&
&&&&&&&&&&&&& &--&
&&&&&&&&&&&&& &bean
&&&&&&&&&&&&& class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" /&
&&&&&&&&&& &/list&
&&&&&& &/property&
测试并运行,此时返回的应该是用户的id了。
2、返回更多用户的信息
&&& 服务器,在返回给客户端用户信息时,默认只返回用户名(我们已经修改成ID).但有时我们需要更多的属性信息,如用户名。
则应做如下修改:
用户登录成功以后,CAS使用一个credentialsToPrincipalResolvers将credentials转成Principal对象,此对象只有一个实现类如下:
SimplePrincipal的构造方法接收两个参数,一个是用户的id,一个为用户的其他属性。用户的ID默认为用户登录时使用的用户名,前面第4点已经讲过如何将用户的name换成用户的id返回给客户端。为了给客户端返回更多的属性,我们必须要给Principal的构造方法传递第二个参数,它是一个Map&String,Object&类型。
具体代码如下:
上图通过给SimplePrincipal传递第二个构造参数设置了更多的属性。
但,它并不会马上显示到客户端,如果要显示到客户端,因为服务器验证成功以后,是通过xml形式将结果传递给客户端的,xml的生成由casServiceValidationSuccess.jsp文件负责。它的具体构造应该是以下形式:
&cas:serviceResponse
&&& xmlns:cas='http://www.yale.edu/tp/cas'&
&&& &cas:authenticationSuccess&
&&&&&& &cas:user&U001&/cas:user&
&&&&&& &cas:attributes&
&&&&&&&&&&&&& &cas:pwd&1234&/cas:pwd&
&&&&&&&&&&&&& &cas:username&Jack&/cas:username&
&&&&&& &/cas:attributes&
&&& &/cas:authenticationSuccess&
&/cas:serviceResponse&
在上面的代码中,cas:attributes元素是我自己添加的,客户端的的Filter在接收到上述的XML以后,会将css:attributes中的属性解析出来,放到AttirubtePrincipal的attributes属性中去(或是放到Asseration的attributes中去,两个只会放一个)。
默认情况下,将所有属性信息放到AttributePrincipal中去,所以在客户端的页面上可以通过以下方式获取值:
所以,组成上面的&cas&:attributes&元素中的内容,就成了如何传递更多属性的关键,在修改了MyCredentialsToPrincipalResolver的代码以后,然后还必须要修改casServiceValidationSuccess.jsp的代码如下:
以下是源代码:
&%@ page session="false" contentType="text/ charset=UTF-8"%&&%@ taglib
&&& prefix="c" uri="/jsp/jstl/core"%&&%@ taglib
&&& uri="/jsp/jstl/functions" prefix="fn"%&&cas:serviceResponse
&&& xmlns:cas='http://www.yale.edu/tp/cas'&
&&& &cas:authenticationSuccess&
&&& &cas:user&${fn:escapeXml(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.id)}&/cas:user&
&&&&&& &c:if test="${not empty pgtIou}"&
&&&&&&&&&& &cas:proxyGrantingTicket&${pgtIou}&/cas:proxyGrantingTicket&
&&&&&& &/c:if&
&&&&&& &c:if test="${fn:length(assertion.chainedAuthentications) & 1}"&
&&&&&&&&&& &cas:proxies&
&&&&&&&&&&&&& &c:forEach var="proxy" items="${assertion.chainedAuthentications}"
&&&&&&&&&&&&&&&&& varStatus="loopStatus" begin="0"
&&&&&&&&&&&&&&&&& end="${fn:length(assertion.chainedAuthentications)-2}" step="1"&
&&&&&&&&&&&&&&&&& &cas:proxy&${fn:escapeXml(proxy.principal.id)}&/cas:proxy&
&&&&&&&&&&&&& &/c:forEach&
&&&&&&&&&& &/cas:proxies&
&&&&&& &/c:if&
&&&&&& &cas:attributes&
&&&&&&&&&& &c:forEach
&&&&&&&&&& && items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}"
&&&&&&&&&&&&& var="attr"&
&&&&&& &&&&&& &cas:${attr.key}&${attr.value}&/cas:${attr.key}&
&&&&&&&&&& &/c:forEach&
&&&&&& &/cas:attributes&
&&& &/cas:authenticationSuccess&
&/cas:serviceResponse&
然后修改deployerConfigContext.xml文件,将最后一个配置项:serviceRegistryDao中的所有属性全部删除或是注销。
这个bean中的RegisteredServiceImpl的ignoreAttributes属性将决定是否添加attributes属性内容,默认为false:不添加,只有去掉这个配置,
cas server才会将获取的用户的附加属性添加到认证用的Principal的attributes中去。
然后即可以在页面上通过以下方式获取用户的其他属性:
&&&&&& Assertion assertion = AssertionHolder.getAssertion();
&&&&&& AttributePrincipal ap =&
assertion.getPrincipal();&& //获取AttributePrincipal对象,这是客户端对象
&&&&&& String name = ap.getName();
&&&&&& Map&String,Object& att = ap.getAttributes();     //获取属性值,为一个Map类型。
&&&&&& out.print("&br/&"+name);
&&&&&& out.print("&br/&"+att);
9、处理中文
casServiceValidationSuccess.jsp页面默认编码格式为ISO-8859-1,且在表单提交到客户端页面时,也使用IS0进行编码,为了处理中文,可以在页面上使用URLEncoder对需要传递的中文时行UTF-8编码,然后从客户端取得数据时,再做URLDecoder解码:
casServiceValidationSuccess.jsp页面,真是一个奇怪的页面,由于cas使用手工解析(没有使用任何dom解析,硬编码识别标标签的开始和标签的结束)xml的方式解析xml文件,所有,在修改此文件时,一定要加以注意:
上图的红框部分,必须要紧凑一些,否则会出现解析错误。
以下是源代码:
&%@ page session="false" contentType="text/
charset=UTF-8" import="java.net.URLEncoder"%&
&%@ taglib prefix="c"
uri="/jsp/jstl/core"%&
&%@ taglib uri="/jsp/jstl/functions"
prefix="fn"%&
&cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'&
&&& &cas:authenticationSuccess&
&cas:user&${fn:escapeXml(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.id)}&/cas:user&
&&&&&& &c:if test="${not empty pgtIou}"&
&&&&&&&&&& &cas:proxyGrantingTicket&${pgtIou}&/cas:proxyGrantingTicket&
&&&&&& &/c:if&
&&&&&& &c:if test="${fn:length(assertion.chainedAuthentications)
&&&&&&&&&& &cas:proxies&
&&&&&&&&&&&&& &c:forEach var="proxy"
items="${assertion.chainedAuthentications}"
&&&&&&&&&&&&&&&&& varStatus="loopStatus" begin="0"
&&&&&&&&&&&&&&&&& end="${fn:length(assertion.chainedAuthentications)-2}"
&&&&&&&&&&&&&&&&& &cas:proxy&${fn:escapeXml(proxy.principal.id)}&/cas:proxy&
&&&&&&&&&&&&& &/c:forEach&
&&&&&&&&&& &/cas:proxies&
&&&&&& &/c:if&
&&& &&& &cas:attributes&
&&&&&&&&&& &c:forEach items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}"
var="attr"&
&&&&&&&&&&&&&&&&& &c:set var="val" value="${attr.value}"/&
&&&&&&&&&&&&&&&&& &cas:${attr.key}&&%=URLEncoder.encode((String)pageContext.getAttribute("val"),"UTF-8")%&&/cas:${attr.key}&
&&&&&&&&&& &/c:forEach&
&&&&&& &/cas:attributes&
&&& &/cas:authenticationSuccess&
&/cas:serviceResponse&
经过编码以后的XML数据如下:
&cas:serviceResponse
xmlns:cas='http://www.yale.edu/tp/cas'&
&&& &cas:authenticationSuccess&
&&&&&& &cas:user&U003&/cas:user&
&&& &&& &cas:attributes&
&&&&&&&&&&&&&&&&& &cas:pwd&1111&/cas:pwd&
&&&&&&&&&&&&&&&&& &cas:username&%E5%BC%A0%E4%B8%89&/cas:username&
&&&&&& &/cas:attributes&
&&& &/cas:authenticationSuccess&
&/cas:serviceResponse&
可见,对中文进行了UTF-8编码。
在客户端使用URLDecoder进行解码:
&&&&&& Assertion assertion = AssertionHolder.getAssertion();
&&&&&& AttributePrincipal ap =&
assertion.getPrincipal();
&&& &&& String id =
ap.getName();
&&&&&& Map&String,Object& att = ap.getAttributes();
&&&&&& out.print("&br/&"+id);
&&&&&& out.print("&br/&"+att);
&&&&&& String name =
URLDecoder.decode(""+att.get("username"),
&&&&&& out.println("&br/&"+name);
显示效果如下:
10、通过监听器将从服务器上返回的数据封装成自己的对象放到Session中去
&&& 从服务器返回信息成功后,将以_const_cas_assertion_为key将Assertion对象放到Session中去。知道了这一点,即可以监听Session的属性添加事件:
package cn.itcast.
import java.net.URLD
import java.util.M
import javax.servlet.http.HttpSessionAttributeL
import javax.servlet.http.HttpSessionBindingE
org.jasig.cas.client.util.AbstractCasF
org.jasig.cas.client.validation.A
&* 通过监听器将从服务器上返回的信息放到Session中
&* @author 传智播客
public class AssertionListener implements HttpSessionAttributeListener {
&&& public void attributeAdded(HttpSessionBindingEvent se) {
&&&&&& if (se.getName().equals(AbstractCasFilter.CONST_CAS_ASSERTION)) {
&&&&&&&&&& System.err.println("添加了某属性.....");
&&&&&&&&&& //不可以使用工具类,只可以使用session获取对象
&&&&&&&&&& Assertion ass = (Assertion)se.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
&&&&&&&&&& Map&String, Object& user =
ass.getPrincipal().getAttributes();
&&&&&&&&&& se.getSession().setAttribute("user", user);
&&&&&&&&&& try {
&&&&&&&&&&&&& for (String key : user.keySet()) {
&&&&&&&&&&&&&&&&& //因为服务器上转码,所以此外解码
&&&&&&&&&&&&&&&&& user.put(key,
&&&&&&&&&&&&&&&&&&&&&&&& URLDecoder.decode("" +
user.get(key), "UTF-8"));
&&&&&&&&&&&&& }
&&&&&&&&&& } catch (Exception e) {
&&&&&&&&&&&&& e.printStackTrace();
&&&&&&&&&& }
&&& public void attributeRemoved(HttpSessionBindingEvent se) {
&&& public void attributeReplaced(HttpSessionBindingEvent se) {
配置到客户端的web.xml中去:
&listener&
&&&&&& &listener-class&cn.itcast.listener.AssertionListener&/listener-class&
&/listener&
然后即可以在客户端的页面上通过以下方式获取值:
11、认定对象:Assertion。
&&&&&& 里面又包含了Principal对象及创建时间、过期时间、和用户的ID或是名称。
&&&&&& 首先,用户的ID或是用户名,在CAS的客户端程序中,可以通过request.getRemoteUser()的方式获取得到。Spring是通过在过虑器中,包装HttpRequest的方式实现的,其实,仍然是从Assertion中获取得到的数据。
&&&&&& 在CAS服务端,用户注册成功以后,CAS服务器端是通过POST方式给客户端传递一个XML数据的方式获取得到数据的。
Assertion的源代码如下:
public interface Assertion extends Serializable {
&&&& * The date from which the assertion is
valid from,有效时间从什么时间开始
&&& Date getValidFromDate();
&&&& * The date which the assertion is valid
until,在效时间,到什么时间结束。
&&& Date getValidUntilDate();
&&&& * The key/value pairs associated with this
assertion,一组属性值
&&& Map getAttributes();
&&&& * The principal for which this assertion
is valid,被代理的对象,此对象中,又包含了一个ID和一组属性值
&&& AttributePrincipal getPrincipal();
通过上面的源代码,可以知道Assertion和Principal的关系如下:
从Assertion中获取信息,可以查看示例客户端的getpt.jsp页面上的代码:
方法1、从Session中获取Assertion对象:
Assertion assertion1 =
(Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
方法2、通过AssertionHolder的静态方法获取Assertion
Assertion assertion2 =
AssertionHolder.getAssertion();
12、单点注销
在一个客户端注销以后,应该将其他所有站点的登录Session全部注销:
 1、发出注销申请
在CAS的客户端,CAS使用一个Map维护了所有登录用户的Session和TG(凭据)。CAS服务器将依次将客户端发送请求,被CAS客户端的注销过虑器拦截到,注销过虑器完成客户端Session的注销工作。
所以,为了实现单点注销,必须要将客户端的单点注销过虑器也打开:
&&&&&& 找到cas客户端web.xml文件,启用以下代码:
&&&&&& &!-- Sign out not yet
implemented,单点注销 --&
&&&&&& &filter&
&&&&&&&&&&&&& &filter-name&CAS
Single Sign Out Filter&/filter-name&
&&&&&&&&&&&&& &filter-class&org.jasig.cas.client.session.SingleSignOutFilter&/filter-class&
&&&&&& &/filter&
&&&&&& &filter-mapping&
&&&&&&&&&&&&& &filter-name&CAS Single Sign
Out Filter&/filter-name&
&&&&&&&&&&&&& &url-pattern&/*&/url-pattern&
&&&&&& &/filter-mapping&
只要打开上面的过虑器,即可以实现单点注销。
测试:在CAS服务器端输入&:即可以完成单点注销。
注意,应该是服务器的地址,即http://服务器地址/logout
单点注销:
TicketGrantingTicketImpl.logOutOfService方法将会获取所有Ticket,通过遍历,然后调用下面的方法。
AbstractWebApplicationService.
logOutOfService方法将会出送一段XML文本给每一个客户端。
以下是TicketGrantingTicketImpl的片段代码:
private void logOutOfServices() {
&&&&&&& for (final Entry&String, Service& entry : this.services.entrySet()) {//遍历所有注册过的客户端
&&&&&&&&&&& if (!entry.getValue().logOutOfService(entry.getKey()))
{  //调用注销服务
&&&&&&&&&&&&&&& LOG.warn("Logout message not sent to [" + entry.getValue().getId() + "];
Continuing processing...");&&
&&&&&&&&&&& }
以下是AbstractWebApplicationService的片段代码:
public synchronized boolean logOutOfService(final String
sessionIdentifier) {
&&&&&&& if (this.loggedOutAlready) {
&&&&&&&&&&& return true;
&&&&&&& LOG.debug("Sending logout request for: " + getId());
&&&&&& //组织一段XML文本
&&&&&&& final String logoutRequest = "&samlp:LogoutRequest
xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\""
&&&&&&&&&&& + GENERATOR.getNewTicketId("LR")
&&&&&&&&&&& + "\" Version=\"2.0\" IssueInstant=\"" + SamlUtils.getCurrentDateAndTime()
&&&&&&&&&&& + "\"&&saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\"&@NOT_USED@&/saml:NameID&&samlp:SessionIndex&"
&&&&&&&&&&& + sessionIdentifier + "&/samlp:SessionIndex&&/samlp:LogoutRequest&";
&&&&&&& this.loggedOutAlready = true;
&&&&&&& if (this.httpClient != null) {
&&&&&&&&&& &return this.httpClient.sendMessageToEndPoint(getOriginalUrl(), logoutRequest, true);  //发送请求
&&&&&&& return false;
13、CAS单点登录流程图
14、完成自己的登录页面
在开始之前,读者必须了解Spring的MVC框架和Spring WebFlow(页面流)。
&&& 然后将完整的cas-server.war包,部署到MyEclipe工作区中,以便于更新和修改。为此,应该将原来classes目录下的cas的原类打包,放到lib目录下。
修改以下页面,可以实现自定义登录页面的需求。具体jsp的写法略。
阅读(...) 评论()

我要回帖

更多关于 如何登陆icloud 的文章

 

随机推荐