单台web app 做登陆很简单,放到session里就可以。但稍微大的应用都是apache下挂着多台tomcat的,tomcat1的session 到tomcat2下就不管用了。tomcat间的session复制也比较费性能,我们尽量要保持web层的无状态。
上一篇apache_tomcat负载均衡配置完成后,我基于此业余做了基于struts的多台应用登陆/自动登陆处理,源码在附件中。
原理:
1、登陆后把信息用户名及密码加密放入cooekis(如果选择自动登陆),同时把User 对象放入session。下上文中通过session.getAttribute("User")来判断是否登陆
2、在访问时会有两个拦截器来处理
a.loginInterceptor
从cookies中取得加密过的username和password,验证是否正确,将Uesr放入session
b.authInterceptor
对需要登陆才能有的操作加入验证处理,如果没登陆将跳转到登陆页,登陆完成后跳转要操作的页面。
代码实现:
1、LoginInterceptor
/**
*
* 从cookies中恢复登陆
*
* @author 锅巴
* @version 1.0 2010-6-29
*/
public class LoginInterceptor implements Interceptor {
public final static String CRYPTO_PWD = "865azo@44536_t";
private UserDAO userDAO;
public String intercept(ActionInvocation arg0) throws Exception {
printCookies();
process(arg0);
return arg0.invoke();
}
private void process(ActionInvocation arg0) throws Exception {
Map session = arg0.getInvocationContext().getSession();
if(session != null && session.get(Statics.USER_SESSION_KEY) != null){
System.out.println("========== user is have in session ");
return ;
}
HttpServletRequest request = ServletActionContext.getRequest();
Cookie[] cookies = request.getCookies();
if(cookies == null)
return;
for (Cookie cookie : cookies) {
if (Statics.COOKIE_REMEMBERME_KEY.equals(cookie.getName())
&& !StringUtils.isEmpty(cookie.getValue())) {
String[] split = cookie.getValue().split("-");
String userName = DESCrypto.decrypt(split[0],CRYPTO_PWD);
String password = DESCrypto.decrypt(split[1],CRYPTO_PWD);
try {
User user = userDAO.attemptLogin(userName, password);
session.put(Statics.USER_SESSION_KEY, user);
} catch (UserNotFoundException e) {
}
}
}
}
private void printCookies(){
System.out.println("========= printCookies statrt ========");
Cookie[] cookies = ServletActionContext.getRequest().getCookies();
if(cookies != null){
for(Cookie cook : cookies){
System.out.println(cook.getName() + " : " + cook.getValue() + " : " + cook.getMaxAge() );
}
}
System.out.println("========= printCookies end ========");
}
public UserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
public void destroy() {
// TODO Auto-generated method stub
}
public void init() {
// TODO Auto-generated method stub
}
}
2、AuthInterceptor
/**
* 必须验证要登陆的拦截器
* @author 锅巴
* @version 1.0 2010-6-29
*/
public class AuthInterceptor implements Interceptor{
private final static String ACTH_ERROR_TO_URL = "forwardLogin";
private static Log log = LogFactory.getLog(AuthInterceptor.class);
public void destroy() {
// TODO Auto-generated method stub
}
public void init() {
// TODO Auto-generated method stub
}
public String intercept(ActionInvocation arg0) throws Exception {
// TODO Auto-generated method stub
Map session = arg0.getInvocationContext().getSession();
if(session == null || session.get(Statics.USER_SESSION_KEY) == null){
return getAuthErrorReturn(arg0);
}
return arg0.invoke();
}
private String getAuthErrorReturn(ActionInvocation arg0){
HttpServletRequest request = ServletActionContext.getRequest();
String urlValue = request.getRequestURL().toString();
if (request.getQueryString() != null)
urlValue += "?" + request.getQueryString();
request.setAttribute(Statics.AUTH_LOGIN_GO, urlValue);
log.debug("urlValue:" + urlValue);
return ACTH_ERROR_TO_URL;
}
}
3、Login 登陆action
/**
*
* 描述
*
* @author 锅巴
* @version 1.0 2010-7-5
*/
public class LoginAction extends ActionSupport implements SessionAware,CookiesAware,ServletResponseAware,ServletRequestAware{
private Map session;
private Map cookie;
private HttpServletResponse response;
private UserDAO userDAO;
private String username;
private String password;
private String goUrl;
private HttpServletRequest request;
private boolean rememberMe;
public boolean isRememberMe() {
return rememberMe;
}
public void setRememberMe(boolean rememberMe) {
this.rememberMe = rememberMe;
}
@Override
public void setServletRequest(HttpServletRequest arg0) {
// TODO Auto-generated method stub
this.request = arg0;
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
public String getGoUrl() {
return goUrl;
}
public void setGoUrl(String goUrl) {
this.goUrl = goUrl;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public void setServletResponse(HttpServletResponse arg0) {
// TODO Auto-generated method stub
this.response = arg0;
}
@Override
public void setSession(Map arg0) {
// TODO Auto-generated method stub
this.session = arg0;
}
@Override
public void setCookiesMap(Map arg0) {
// TODO Auto-generated method stub
this.cookie = arg0;
}
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
return SUCCESS;
}
public String postLogin() throws Exception {
User user = null;
try{
user = userDAO.attemptLogin(username, password);
}catch(UserNotFoundException ex){
addActionError("登陆失败!");
return INPUT;
}
processCookie(user);
return SUCCESS;
}
private void processCookie(User user) throws Exception {
session.put(Statics.USER_SESSION_KEY, user);
//cookie value 不能有==这种情况,不能ie会拒绝该cookies
Cookie cookie = new Cookie(Statics.COOKIE_REMEMBERME_KEY, DESCrypto.desCrypto(username.getBytes(), LoginInterceptor.CRYPTO_PWD)+ "-" + DESCrypto.desCrypto(password.getBytes(), LoginInterceptor.CRYPTO_PWD));
if (rememberMe){
cookie.setMaxAge(60 * 60 * 24 * 14);
}else{
cookie.setMaxAge(0);
}
cookie.setPath("/");
cookie.setDomain("t.com");
response.addCookie(cookie);
}
}
4、LoginOut 退出action
/**
*
* 描述
*
* @author 锅巴
* @version 1.0 2010-7-5
*/
public class LoginOutAction extends ActionSupport implements ServletResponseAware,ServletRequestAware{
private HttpServletResponse response;
private HttpServletRequest request;
@Override
public void setServletResponse(HttpServletResponse arg0) {
// TODO Auto-generated method stub
this.response=arg0;
}
@Override
public void setServletRequest(HttpServletRequest arg0) {
// TODO Auto-generated method stub
this.request = arg0;
}
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
Cookie[] cookies = request.getCookies();
if (cookies!=null) {
for (Cookie cookie : cookies) {
if (Statics.COOKIE_REMEMBERME_KEY.equals(cookie
.getName())) {
//清除cookie时要与增加时步骤一致 如path,domain
cookie.setPath("/");
cookie.setDomain("t.com");
cookie.setValue("");
cookie.setMaxAge(0);
response.addCookie(cookie);
}
}
}
HttpSession session = request.getSession(false);
if (session!=null)
session.removeAttribute(Statics.USER_SESSION_KEY);
System.out.println("============== login out ================");
return SUCCESS;
}
}
5、struts.xml配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="loginInterceptor" class="com.my.interceptor.LoginInterceptor"></interceptor>
<interceptor name="authInterceptor" class="com.my.interceptor.AuthInterceptor"></interceptor>
<!-- 默认拦截器堆栈 -->
<interceptor-stack name="loginDefaultStack">
<interceptor-ref name="loginInterceptor"></interceptor-ref>
<interceptor-ref name="authInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
<!-- 需要登陆验证的拦截器堆栈 -->
<interceptor-stack name="autowireDefault">
<interceptor-ref name="loginInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="autowireDefault"></default-interceptor-ref>
<global-results>
<!-- 验证未通过跳转全局result -->
<result name="forwardLogin" type="chain">
<param name="actionName">forwardLogin</param>
<param name="namespace">/common</param>
</result>
</global-results>
</package>
<include file="struts-conf/common.struts.xml"></include>
</struts>
6、common.struts.xml 配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="common" namespace="/common" extends="default">
<!-- 跳转登录 -->
<action name="forwardLogin" class="com.my.action.JumpLoginAction">
<result name="success" type="redirect">
/common/login.html?goUrl=${goUrl}
</result>
</action>
<!-- 登陆页面 -->
<action name="login" class="com.my.action.LoginAction">
<result name="success" type="dispatcher">
/common/login.jsp
</result>
</action>
<!-- 退出 -->
<action name="loginOut" class="com.my.action.LoginOutAction">
<result name="success" type="dispatcher">
/common/login_out.jsp
</result>
</action>
<!-- 登陆post操作 -->
<action name="postLogin" class="com.my.action.LoginAction" method="postLogin">
<result name="success" type="redirect">
${goUrl}
</result>
<result name="input" type="dispatcher">
/common/login.jsp
</result>
<interceptor-ref name="autowireDefault" />
<interceptor-ref name="validationWorkflowStack" />
</action>
<!-- 需要登陆验证的页面 -->
<action name="home" class="com.my.action.HomeAction">
<result name="success" type="dispatcher">
/index.jsp
</result>
<interceptor-ref name="loginDefaultStack" />
</action>
</package>
</struts>
注意事项:
1、LoginAction-postLogin-validation.xml ,可以按action 方法名来验证。
2、cookies 中不能含有"==",否则IE会写不进去。
3、写cookies与清cookies的操作要完全一致,否则清不了。
如:
写cookies
//cookie value 不能有==这种情况,不能ie会拒绝该cookies
Cookie cookie = new Cookie(Statics.COOKIE_REMEMBERME_KEY, DESCrypto.desCrypto(username.getBytes(), LoginInterceptor.CRYPTO_PWD)+ "-" + DESCrypto.desCrypto(password.getBytes(), LoginInterceptor.CRYPTO_PWD));
if (rememberMe){
cookie.setMaxAge(60 * 60 * 24 * 14);
}else{
cookie.setMaxAge(0);
}
cookie.setPath("/");
cookie.setDomain("t.com");
response.addCookie(cookie);
清cookies
cookie.setPath("/");
cookie.setDomain("t.com");
cookie.setValue("");
cookie.setMaxAge(0);
response.addCookie(cookie);