前言
到目前位置,我们的用户模块和角色模块基本已经做好了,我们的纳税服务系统是放在一个大系统里边的。我们应该把我们已经写好的模块加载进去。
本文主要的知识点:
- 配置系统首页
- 登陆模块
- 权限拦截模块
加载整个系统首页
导入对应的JSP页面:
这里写图片描述
创建home模块的包,对应的Action和struts配置文件
这里写图片描述
效果图:
这里写图片描述
把纳税服务系统加到总系统上
当我点击纳税服务的时候,会出现纳税服务子系统的页面
这里写图片描述
绑定相关的超链接,跳转到对应的Action中
代码语言:javascript复制 <li><a href="${ctx }/nsfw/home_frame.action">纳税服务</a> </li>
导入对应的JSP页面:
这里写图片描述
创建对应的模块包和配置文件:
这里写图片描述
代码语言:javascript复制public class HomeAction {
public String frame() {
return "frame";
}
public String left() {
return "left";
}
public String top() {
return "top";
}
}
配置文件
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="nsfw-home" namespace="/nsfw" extends="struts-default">
<action name="nsfw_*" class="zhongfucheng.nsfw.HomeAction" method="{1}">
<result name="{1}">/WEB-INF/jsp/nsfw/{1}.jsp</result>
</action>
</package>
</struts>
- 效果:
这里写图片描述
子功能添加到纳税服务系统中
就是让左边的导航栏跳转到我们的对应显示页面就行了。
代码语言:javascript复制 <dl class="">
<dt><a class="yh" href="${basePath}role/role_listUI.action" target="mainFrame"><b></b>角色管理<s class="down"></s>
</a></dt>
</dl>
<dl class="">
<dt><a class="yh" href="${basePath}user/user_listUI.action" target="mainFrame"><b></b>用户管理<s class="down"></s>
</a></dt>
</dl>
到目前为止,我们已经写了用户模块和角色模块了。也已经把系统的首页和子系统的首页配置好了。我们的系统是需要登陆后才能访问的。
这里写图片描述
因此,接下来主要讲解登陆模块、权限拦截
登陆模块
编写Action与配置文件
引入对应的JSP页面
这里写图片描述
编写Action处理请求和对应的配置文件:
这里写图片描述
效果:
这里写图片描述
Action处理
在常量类中保存着一个Session的标识:
代码语言:javascript复制 /***********保存Session域的常量***********/
public static String USER = "SYS_USER";
Action
代码语言:javascript复制package zhongfucheng.login;
import com.opensymphony.xwork2.ActionContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import zhongfucheng.core.action.BaseAction;
import zhongfucheng.core.constant.Constant;
import zhongfucheng.user.entity.User;
import zhongfucheng.user.service.UserService;
import java.util.List;
/**
* Created by ozc on 2017/6/2.
*/
public class LoginAction extends BaseAction {
/***************封装数据**********************/
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
/****************调用userService****************************/
@Autowired
private UserService userServiceImpl;
public String loginUI() {
return "loginUI";
}
/****************记载着登陆状态信息*********************/
private String loginResult;
public String getLoginResult() {
return loginResult;
}
public void setLoginResult(String loginResult) {
this.loginResult = loginResult;
}
/*****************业务方法******************/
public String login() {
if (user != null) {
List<User> list = userServiceImpl.findUserByAccountAndPassword(user.getAccount(), user.getPassword());
//如果查到有值,那么就证明有该用户的,给他登陆
if (list != null && list.size() > 0) {
//保存到Session域中,为了更方便用,我们使用常量保存。
ActionContext.getContext().getSession().put(Constant.USER, list.get(0));
//保存到日志文件中Log
Log log = LogFactory.getLog(getClass());
log.info("用户名称为" list.get(0).getName() "登陆了系统!");
//重定向到首页
return "home";
} else {
//登陆失败,记载登陆信息
loginResult = "登陆失败了,用户名或密码错误了";
}
}
//只要不成功的,都回到登陆页面
return loginUI();
}
}
在dao层中实现根据账户和密码查询数据:
代码语言:javascript复制 @Override
public List<User> findUserByAccountAndPassword(String account, String password) {
//通过账户和密码查找对象
String sql = "FROM User WHERE account=? AND password=?";
return getSession().createQuery(sql).setParameter(0, account).setParameter(1,password).list();
}
我们还在Action中定义了一个String来记载着Login的状态,如果出错了。我们就返回loginUI界面,然后在那里回显Login出错的信息。
同时登录完在系统主页上回显数据:
代码语言:javascript复制 <a><b></b><font color="red">欢迎您,<s:property value="%{#session.SYS_USER.account}"/> </font></a>
这里写图片描述
注销功能
在主页上的退出上绑定超链接
代码语言:javascript复制 <td align="left" valign="middle" ><a href="${ctx}/sys/login_logout.action">退出</a></td>
Action处理:
代码语言:javascript复制 public String logout() {
//销毁session的值
ActionContext.getContext().getSession().remove(Constant.USER);
return loginUI();
}
处理首页
当用户访问首页的时候,我们重定向到登陆页面:
- 访问Index页面的时候,我们就重定向到登陆页面上。
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() "://" request.getServerName() ":" request.getServerPort() path "/";
response.sendRedirect(basePath "sys/login_loginUI.action");
%>
过滤器模块
进入系统拦截
我们讲道理是要用户登陆后,才能访问我们的总系统。但是现在假如用户知道了我们的首页地址,他可以直接访问我们的首页地址而不用登陆。这是不合适的。
因此,我们写一个过滤器进行拦截,如果用户不是想要登陆,而访问我们其他的页面。都拦截他,让他登陆后才能访问。
这里写图片描述
过滤器:
代码语言:javascript复制package zhongfucheng.core.filter;
import zhongfucheng.user.entity.User;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by ozc on 2017/6/4.
*/
public class LoginFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//得到用户访问的路径
String uri = request.getRequestURI();
//登陆路径
String loginPath = request.getContextPath() "/sys/login_login.action";
//判断用户访问的是哪里
if (!uri.contains("login_")) {//如果不是访问我们的登陆模块
//判断该用户是否登陆了。
User user = (User) request.getSession().getAttribute("SYS_USER");
if (user == null) {//如果在session找不到,那么就是没有登陆
//没有登陆,跳转到登陆页面
response.sendRedirect(loginPath);
return;
} else {//有用户信息,就是登陆了。
//放行
chain.doFilter(request, response);
}
} else {//如果是访问我们的登陆模块,放行
chain.doFilter(request, response);
}
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
配置过滤器,需要在struts过滤器之前配置:
代码语言:javascript复制 <filter>
<filter-name>LoginFilter</filter-name>
<filter-class>zhongfucheng.core.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
权限过滤
我们的纳税服务子系统并不是任何人都可以进去操作的,我们有可以对角色的管理,对用户的管理。。一般的用户是没有权限去操作这些东西的。因此,我们要对其进行权限控制。
当该用户有权限才能够访问纳税服务系统的内容,没有权限就不给该用户看。
权限过滤的前提条件:
- 用户已经登陆了
- 知道用户访问的是什么子系统
由于我们在LoginFilter中已经可以得到这两个条件了,于是我们在LoginFilter中接着写就行了。又因为权限过滤是一个比较单独的模块,我们可以将其抽出。这样一来,LoginFilter又不会显得太大,职责又分工了。
这里写图片描述
过滤器全部代码:WebApplicationContextUtils得到IOC中的对象
代码语言:javascript复制package zhongfucheng.core.filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import zhongfucheng.core.utils.PermissionCheck;
import zhongfucheng.user.entity.User;
import zhongfucheng.user.service.UserService;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by ozc on 2017/6/4.
*/
public class LoginFilter implements Filter {
//注入userService
@Autowired
private UserService userServiceImpl;
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//得到用户访问的路径
String uri = request.getRequestURI();
//登陆路径
String loginPath = request.getContextPath() "/sys/login_login.action";
//提示页面
String warningPath = request.getContextPath() "/sys/login_noPermissionUI.action";
//定义User变量
User user;
//判断用户访问的是哪里
if (!uri.contains("login_")) {//如果不是访问我们的登陆模块
//判断该用户是否登陆了。
user = (User) request.getSession().getAttribute("SYS_USER");
if (user == null) {//如果在session找不到,那么就是没有登陆
//没有登陆,跳转到登陆页面
response.sendRedirect(loginPath);
return;
} else {//有用户信息,就是登陆了。
if (uri.contains("nsfw")) {//如果访问纳税服务系统,就要有对应的权限
//用户已经登陆了,判断用户有没有权限访问子系统
//得到IOC容器中的对象
WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
PermissionCheck permissionCheck = (PermissionCheck)applicationContext.getBean("permissionCheck");
if (permissionCheck.check(user, "nsfw")) {//有权限
//放行
chain.doFilter(request, response);
} else {//没有权限
//返回到提示页面
response.sendRedirect(warningPath);
}
} else {//可以不用权限,直接放行
//放行
chain.doFilter(request, response);
}
}
} else {//如果是访问我们的登陆模块,放行
chain.doFilter(request, response);
}
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
在登陆完之后,就查询出用户拥有的所有角色,并设置到该用户中:
代码语言:javascript复制 public String login() {
if (user != null) {
List<User> list = userServiceImpl.findUserByAccountAndPassword(user.getAccount(), user.getPassword());
//如果查到有值,那么就证明有该用户的,给他登陆
if (list != null && list.size() > 0) {
//查出用户所有的权限,设置到User中
User user = list.get(0);
List<UserRole> roles = userServiceImpl.findRoleById(user.getId());
user.setUserRoles(roles);
//保存到Session域中,为了更方便用,我们使用常量保存。
ActionContext.getContext().getSession().put(Constant.USER, user);
//保存到日志文件中Log
Log log = LogFactory.getLog(getClass());
log.info("用户名称为" list.get(0).getName() "登陆了系统!");
//重定向到首页
return "home";
} else {
//登陆失败,记载登陆信息
loginResult = "登陆失败了,用户名或密码错误了";
}
}
//只要不成功的,都回到登陆页面
return loginUI();
}
在User.java中加入一个List集合,存储着用户所拥有的角色
代码语言:javascript复制 //得到用户所有的角色
private List<UserRole> userRoles;
public List<UserRole> getUserRoles() {
return userRoles;
}
public void setUserRoles(List<UserRole> userRoles) {
this.userRoles = userRoles;
}
到这里,有同学可能会疑问,为啥现在我要修改User的结构呢??明明在编写User和Role的时候说好不修改User类的。我们在验证的时候需要得到用户所有的角色,从而得到权限。如果在检查的时候做的话,我们用的是过滤器检查,每请求一次都要去访问数据库。
这样的话就非常耗费我们的性能,于是我们就修改User类,但这次的修改没有影响到我们其他地方的操作。这样一来,我们在检查的时候就可以通过对象来得到用户对应的权限了,不用查询数据库。
检查用户是否有权限:
代码语言:javascript复制public class PermissionCheck {
private User user;
private String code;
@Autowired
private UserService userServiceImpl;
public boolean check(User user, String code) {
this.user = user;
this.code = code;
//得到该用户的所有权限
List<UserRole> userRoles = user.getUserRoles();
if (userRoles == null) {
userRoles = userServiceImpl.findRoleById(user.getId());
}
//遍历初用户拥有的角色,看看有没有对应的权限
for (UserRole userRole : userRoles) {
Role role = userRole.getUserRoleId().getRole();
//得到角色所拥有的权限
Set<RolePrivilege> rolePrivilegeSet = role.getRolePrivilegeSet();
//遍历权限,看看有没有nsfw的权限
for (RolePrivilege privilege : rolePrivilegeSet) {
String code1 = privilege.getCompositeKey().getCode();
if (code1.equals(code)) {//如果该用户有权限
return true;
}
}
}
//遍历完都没有return true,那么就是没有权限了。
return false;
}
}
页面嵌套问题
现在我打开了两个首页,是同一个会话的。如果用户太久没有操作我们的页面,那么Session就会被摧毁。
这里写图片描述
等用户再操作的时候,Session已经被Tomcat摧毁了。讲道理用户操作页面的时候,是会回到登陆页面的。我们看看发生了什么:
这里写图片描述
登陆页面嵌套在我们右边的显示页面了。为啥出现这种情况??
- 当用户的Session没有了,用户操作时,过滤器就会将页面跳转到登陆页面
- 而我们点击了左边的菜单栏,默认在右边显示。
所以,到目前为止,我们的逻辑是没毛病的。但怎么解决上面遇到的情况呢??** 我们不需要使用监听器Session,监听Session被摧毁了,然后刷新页面**。。我们用更好地一种解决办法:
- 判断自身页面是否为顶级窗口,如果不是就自动刷新父窗口的地址,跳转到顶级窗口中。
/*如果该页面不是顶级窗口,那么就自动刷新一下到父窗口中*/
if(window!=window.parent) {
window.parent.location.reload(true);
}
总结
- 对于登陆和注销功能就没什么好说的,我们已经写过很多次了。在登陆的时候就是将我们的User对象保存Session域对象中而已。
- 当用户访问index页面的时候,我们就让它重定向到登陆页面上
- 只有登陆了才能访问我们的系统首页,因此我们编写了一个过滤器。判断该用户是否是要访问我们的登陆页面,如果不是,就判断该用户是否登陆了(也就是判断session有没有User值)。如果有就放行,如果没有就跳转到登陆页面上
- 我们还可以对其进行权限认证,权限认证是基于用户已经登陆的前提下的。对于权限我们直接使用权限的Code来进行校验。为了方便我们去验证,我们在登陆的时候就把用户的权限全都加载到用户上(这样的话,在每次验证就不用重复去找数据库要数据了。)
- session失效的问题导致了页面的嵌套。我们无需监听Session失效的问题,直接在javaScript用代码判断该页面是否是顶级页面,如果不是顶级页面,直接就跳转到顶级页面就行了!