Spring Security2.04+OpenID配置攻略
目 录
1.QQ互联中的OpenID 3
2. 配置环境 3
3、 Spring Security的xml配置 3
4、 OpenID登录页 17
5、 申请OpenID 账号 17
6、 在本地登录OpenID 18
7、 其他注意事项 19
QQ 互联接口中,在QQ登陆成功后,会返回一个OpenID,但是此OpenID是一个大写字母和数字的组合。与一般的OpenID的格式不同,正常的OpenID应该是http开头的URL格式,例如http://zhangsan.openid.org.cn/ ,本文介绍的是国际通用的OpenID认证协议+Spring Security配置过程。
本文的介绍的配置过程是在OpenJWeb2.63环境下配置的,OpenJWeb2.63集成了S2SH和Spring Security2.0.4,大家也可以自己搭建一个S2SH+Spring Security2.04的环境,在环境搭建好以后,我们需要从网上找到openid4java-0.9.8.jar,放到WEB-INF\lib目录下。有了这个驱动,我们就可以在spring security中进行openID的认证登陆。
另外,我们需要将spring-security-openid-2.0.4.jar放到lib目录中。
在web.xml文件中,我们需要增加一段配置以支持openID认证过滤器:
特别注意Spring Security2.0.4的头部配置,因为网上版本众多,有的是以
这种标签。可以参考下面的xml头:
xmlns:p="http://www.springframework.org/schema/p" xmlns:security="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd" default-autowire="byType" default-lazy-init="true"> 网上有很多pdf格式的spring security配置文档,里面介绍了 使用 但是经过试验发现,使用 参考现有的authenticationProcessingFilter的配置方式,我们需要增加以下配置来满足openID认证授权: class="org.springframework.security.util.FilterChainProxy"> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,openIDAuthenticationFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterSecurityInterceptor ]]> 上面蓝字部分是增加了一个openIDAuthenticationFilter ,下面是增加的这个bean的配置: class="org.springframework.security.ui.openid.OpenIDAuthenticationProcessingFilter" p:authenticationManager-ref="authenticationManager" p:authenticationFailureUrl="/secure/openid.jsp" p:defaultTargetUrl="/apps/v22/ui/frame/main.jsp" p:filterProcessesUrl="/j_spring_openid_security_check" /> p:authenticationFailureUrl指明认证失败跳转的页面。p:defaultTargetUrl指明认证成功跳转的页面。p:filterProcessesUrl指明openID的认证过滤器URL. 在authenticationManager Bean中,原来有 class="org.springframework.security.providers.ProviderManager" p:sessionController-ref="concurrentSessionController"> 下面我们要设置openidAuthenticationProvider Bean: class="org.springframework.security.providers.openid.OpenIDAuthenticationProvider">
完整的配置(openjweb中此配置文件为D:\project\openjweb\src\java\applicationContext-security-new.xml): xmlns:p="http://www.springframework.org/schema/p" xmlns:security="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd" default-autowire="byType" default-lazy-init="true"> class="org.springframework.security.util.FilterChainProxy"> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,openIDAuthenticationFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterSecurityInterceptor ]]> class="org.springframework.security.ui.openid.OpenIDAuthenticationProcessingFilter" p:authenticationManager-ref="authenticationManager" p:authenticationFailureUrl="/secure/openid.jsp" p:defaultTargetUrl="/apps/v22/ui/frame/main.jsp" p:filterProcessesUrl="/j_spring_openid_security_check" /> class="org.springframework.security.context.HttpSessionContextIntegrationFilter"/> class="org.springframework.security.ui.logout.LogoutFilter"> class="org.openjweb.core.springsecurity.UserAuthenticationProcessingFilter" p:authenticationManager-ref="authenticationManager" p:authenticationFailureUrl="/common/chooseLogin.jsp?login_error=1" p:defaultTargetUrl="/apps/v22/redirect.jsp" p:filterProcessesUrl="/j_spring_security_check" p:rememberMeServices-ref="rememberMeServices"/> class="org.springframework.security.providers.ProviderManager" p:sessionController-ref="concurrentSessionController"> class="org.springframework.security.concurrent.ConcurrentSessionControllerImpl" p:maximumSessions="1000" p:exceptionIfMaximumExceeded="true" p:sessionRegistry-ref="sessionRegistry" p:messageSource-ref="messageSource"/> class="org.springframework.security.concurrent.SessionRegistryImpl"/> class="org.springframework.context.support.ReloadableResourceBundleMessageSource" p:basename="/WEB-INF/classes/messageResource_zh_CN"/> class="org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter"/> class="org.springframework.security.ui.rememberme.RememberMeProcessingFilter" p:authenticationManager-ref="authenticationManager" p:rememberMeServices-ref="rememberMeServices"/>
class="org.springframework.security.providers.anonymous.AnonymousProcessingFilter" p:key="springsecurity" p:userAttribute="anonymousUser,ROLE_ANONYMOUS"/> class="org.springframework.security.ui.ExceptionTranslationFilter" p:accessDeniedHandler-ref="accessDeniedHandler" p:authenticationEntryPoint-ref="authenticationEntryPoint"/> class="org.springframework.security.ui.AccessDeniedHandlerImpl" p:errorPage="/accessDenied.jsp"/> class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint" p:loginFormUrl="/common/chooseLogin.jsp" p:forceHttps="false"/> class="org.springframework.security.providers.dao.cache.EhCacheBasedUserCache">
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> value="classpath:ehcache-security.xml" />
class="org.springframework.security.intercept.web.FilterSecurityInterceptor"> ref="authenticationManager" /> ref="accessDecisionManager" /> ref="databaseFilterInvocationDefinitionSource" />
class="org.springframework.security.vote.AffirmativeBased"> class="org.springframework.security.vote.RoleVoter">
class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices" p:key="springsecurity" p:userDetailsService-ref="userDetailsService"/> class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
class="org.springframework.security.providers.openid.OpenIDAuthenticationProvider">
class="org.springframework.security.providers.encoding.Md5PasswordEncoder"/> class="org.openjweb.core.springsecurity.UserDetailsServiceImpl">
class="org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource">
type="org.springframework.security.util.UrlMatcher" ref="antUrlPathMatcher" />
class="org.springframework.security.util.AntUrlPathMatcher" /> class="org.openjweb.core.springsecurity.RequestMapFactoryBean" init-method="init">
class="org.springframework.security.event.authentication.LoggerListener"/>
注意配置文件中的这段代码:
class="org.openjweb.core.springsecurity.UserDetailsServiceImpl">
org.openjweb.core.springsecurity.UserDetailsServiceImpl是OpenJWeb中实现Spring Security 的UserDetailsService接口的实现类。我们 此实现类中增加一段代码,此段代码获取openID认证以后的OpenID账号,检查数据库表comm_user中是否存在此账号,如果不存在,则认证成功后自动向数据库表添加一条账号记录,并且为此账号分配一个网站会员角色。下面是org.openjweb.core.springsecurity.UserDetailsServiceImpl的代码:
package org.openjweb.core.springsecurity;
import org.apache.log4j.Logger;
import org.openjweb.core.dao.IBaseDao;
import org.openjweb.core.entity.CommUser;
import org.openjweb.core.entity.CommUserRole;
import org.openjweb.core.service.IDBSupportService;
import org.openjweb.core.service.ServiceLocator;
import org.openjweb.core.util.StringUtil;
import org.springframework.dao.DataAccessException;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UsernameNotFoundException;
public class UserDetailsServiceImpl implements IUserService
{
private static final Logger logger = Logger.getLogger(UserDetailsServiceImpl.class);
private IBaseDao defaultDao;
public UserDetailsServiceImpl (IBaseDao dao)
{
this.defaultDao = dao;
}
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException
{
CommUser user = null;
try
{
user = (CommUser)this.defaultDao.findById(CommUser.class.getName(), "loginId", userName);
if(user.getIsInUse()!=null&&user.getIsInUse().equals("Y"))
{
}
else
{
logger.info("用户被锁定......");
throw new UsernameNotFoundException(userName); //被锁定
}
}
catch(Exception ex)
{
//检查如果是openid格式的,则生成一个对应的openid用户--必填项先默认生成,用户再自己修改
if(userName.startsWith("http://")||userName.startsWith("https://"))
{
IDBSupportService service = (IDBSupportService)ServiceLocator.getBean("IDBSupportService3");
user = new CommUser();
Long serial = null;
try
{
serial = service.getSerial();
}
catch(Exception ex1)
{
//
}
user.setUserId(serial);
user.setRowId(StringUtil.getUUID());
user.setLoginId(userName);
user.setComId("C0001");
user.setIsPortalMember("Y");//设置为网站会员,这种情况一般是网站会员
user.setUsername(userName);//暂时以用户id作为用户名
user.setUserEmail(serial.toString()+"openjweb.com");//生成一个默认的
user.setPassword("unused");
user.setUserMobile(serial.toString());//生成一个默认的由用户自己修改
user.setIsInUse("Y");
user.setEmpNo(serial.toString());//工号
user.setUpdateUid("system");
user.setCreateUid("system");
user.setCreateDt(StringUtil.getCurrentDateTime());
user.setUpdateDt(user.getCreateDt());
user.setCurrFrameCode("06");
user.setCurrSkinCode("sky");
try
{
service.saveOrUpdate(user);
//然后分配门户网站会员角色
CommUserRole roleEnt = new CommUserRole();
roleEnt.setCreateDt(StringUtil.getCurrentDateTime());
roleEnt.setSerialNo(service.getSerial());
roleEnt.setRoleId(new Long(505715));
roleEnt.setUserId(user.getUserId());
service.saveOrUpdate(roleEnt);
}
catch(Exception ex2)
{
}
}
else
{
throw new UsernameNotFoundException(userName);
}
}
return user;
}
}
编写一个jsp,例如openid.jsp,下面是此jsp中的一段form内容:
说明:/portal/指的是openjweb的应用路径,如果在webapps根目录可使用/j_spring_openid_security_check ,OpenID输入框的name必须为j_username。
我们可以到谷歌、微软等openID提供商注册一个openID账号,或者在http://www.openid.org.cn上注册一个账号,例如注册一个baozhengw的账号,则完整的OpenID账号则为: http://baozhengw.openid.org.cn/
运行第四步创建的JSP,见下图:
输入:http://baozhengw.openid.org.cn/
然后点登录,系统会跳转到OpenID服务器(系统会根据openid的域名跳转到对应的服务器),见下图(跳转到http://www.openid.org.cn/):
输入baozhengw,和注册时的口令,然后点登录,自动跳转到http://localhost:8088/portal/apps/v22/ui/frame/main.jsp(在配置文件中通过redirect.jsp跳转到后台,因为新增用户自动添加了关联角色,所以能进入后台):
SecurityContextImpl securityContext =(SecurityContextImpl)obj;
Object tmpObj = null;
tmpObj = securityContext.getAuthentication().getPrincipal();
以传统的方式登录,即/j_spring_security_check本地数据库认证的模式,通过getPrincipal()实际返回的是UserDetailsService接口的实现类,在OpenJWeb中是CommUser类,而使用OpenID,则getPrincipal()返回的是OpenID账号,如http://baozhengw.openid.org.cn/ ,所以产品支持OpenID以后,getPrincipal()的返回值类型处理要考虑多种类型。
OpenJWeb 开源组织
QQ 29803446