首页功能
需求分析
用户访问项目首页,首先进入登录页面。
时序&流程
前端要先访问首页index.jsp再跳转到登录页面login.jsp,但所有页面资源都WEB-INF中不能直接访问,所以要在后端要做两件事,先根据用户输入跳转到首页,再根据首页请求跳转到登录页面
Controller层
IndexController
用户http://127.0.0.1:8080/crm/输入跳转到首页
@Controller
public class IndexController {
/*
理论上,给Controller方法分配url:http://127.0.0.1:8080/crm/
为了简便,协议://ip:port/应用名称必须省去,用/代表应用根目录下的/
*/
@RequestMapping("/")
public String index(){
//请求转发
return "index";
}
}
UserController
接收到settings/qx/user/toLogin.do请求,跳转到login.jsp
@Controller
public class UserController {
/**
* url要和controller方法处理完请求之后,响应信息返回的页面的资源目录保持一致
*/
@RequestMapping("/settings/qx/user/toLogin.do")
public String toLogin(){
//请求转发到登录页面
return "settings/qx/user/login";
}
}
前端页面
index.jsp
在浏览器中输入settings/qx/user/toLogin.do以通过UserController访问login.jsp
<body>
<script type="text/javascript">
window.location.href = "settings/qx/user/toLogin.do";
</script>
</body>
用户登录
需求分析
用户在登录页面,输入用户名和密码,点击"登录"按钮或者回车,完成用户登录的功能.
*用户名和密码不能为空
*用户名或者密码错误,用户已过期,用户状态被锁定,ip受限 都不能登录成功
*登录成功之后,所有业务页面显示当前用户的名称
*实现10天记住密码
*登录成功之后,跳转到业务主页面
*登录失败,页面不跳转,提示信息
时序&流程
后端需要接受前端传入的请求,在数据库中查找对应的用户名和密码,并返回查询结果
笔记1:mybatis逆向工程
1)简介:根据表生成mapper层三部分代码:实体类,mapper接口,映射文件。
2)使用mybatis逆向工程:
a)创建工程:crm-mybatis-generator
b)添加插件:<!--myBatis逆向工程插件--> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> </plugin>
c)添加配置文件:
数据库连接信息
代码保存的目录
表的信息
d)运行mybatis的逆向工程,根据指定表生成java代码,保存到指定的目录中。c)添加配置文件:
数据库连接信息
代码保存的目录
表的信息
d)运行mybatis的逆向工程,根据指定表生成java代码,保存到指定的目录中。
利用mybatis逆向工程生成tbl_user表的三部分代码:实体类,mapper接口,映射文件
Mapper层
UserMapper.java
创建查询封装好的用户名和密码的方法
User selectUserByLoginActAndPwd(Map<String,Object> map);
UserMapper.xml
编写tbl_user表查询语句,查询与用户名和密码对应的用户的所有信息
<select id="selectUserByLoginActAndPwd" parameterType="map" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from tbl_user
where login_act=#{loginAct} and login_pwd=#{loginPwd}
</select>
Service层
UserService.java
创建查询用户的服务
User queryUserByLoginActAndPwd(Map<String, Object> map);
UserServiceImpl.java
调用userMapper中的selectUserByLoginActAndPwd方法实现UserService接口中的queryUserByLoginActAndPwd服务
@Autowired
private UserMapper userMapper;
@Override
public User queryUserByLoginActAndPwd(Map<String, Object> map) {
return userMapper.selectUserByLoginActAndPwd(map);
}
Controller层
UserController.java
笔记2:作用域
把控制层(controller)代码中处理好的数据传递到视图层(jsp),使用作用域传递
pageContext:用来在同一个页面的不同标签之间传递数据。
request:在同一个请求过程中间传递数据。
session: 同一个浏览器窗口的不同请求之间传递数据。(记住密码)
application:所有用户共享的数据,并且长久频繁使用的数据。
笔记3:记住密码
访问:login.jsp---->后台:.html:如果上次记住密码,自动填上账号和密码;否则,不填。
如何判断上次是否记住密码?cookie
–上次登录成功,判断是否需要记住密码:如果需要记住密码,则往浏览器写cookie;否则,删除cookie。
而且cookie的值必须是该用户的loginAct和loginPwd
–下次登录时,判断该用户有没有cookie:如果有,则自动填写账号和密码;否则,不写。
而且填写的是cookie的值.
----->浏览器显示获取cookie:
1,使用java代码获取cookie:
Cookie[] cs=request.getCookies();
for(Cookie c:cs){
if(c.getName().equals(“loginAct”)){
String loginAct=c.getValue();
}else if(c.getName().equals(“loginPwd”)){
String loginPwd=c.getValue();
}
}
2,使用EL表达式获取cookie:
${cookie.loginAct.value}
${cookie.loginPwd.value}
@RequestMapping("/settings/qx/user/login.do")
public @ResponseBody Object login(String loginAct, String loginPwd, String isRemPwd, HttpServletRequest request, HttpServletResponse response, HttpSession session) {
//封装参数
Map<String,Object> map = new HashMap<>();
map.put("loginAct",loginAct);
map.put("loginPwd",loginPwd);
//调用service层方法,查询用户
User user = userService.queryUserByLoginActAndPwd(map);
//根据查询结果,生成响应信息
ReturnObject returnObject = new ReturnObject();
if (user == null) {
//登录失败,用户名或者密码错误
returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
returnObject.setMessage("用户名或者密码错误");
}else {//进一步判断账号是否合法
//user.getExpireTime() //2019-10-20
// new Date() //2020-09-10
if (DateUtils.formateDateTime(new Date()).compareTo(user.getExpireTime()) > 0) {
//登录失败,账号已过期
returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
returnObject.setMessage("账号已过期");
} else if ("0".equals(user.getLockState())) {
//登录失败,状态被锁定
returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
returnObject.setMessage("状态被锁定");
} else if (!user.getAllowIps().contains(request.getRemoteAddr())) {
//登录失败,ip受限
returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
returnObject.setMessage("ip受限");
} else {
//登录成功
returnObject.setCode(Contants.RETURN_OBJECT_CODE_SUCCESS);
//把user加入session
session.setAttribute(Contants.SESSION_USER, user);
Cookie c1=new Cookie("loginAct",user.getLoginAct());
Cookie c2=new Cookie("loginPwd",user.getLoginPwd());
if("true".equals(isRemPwd)){
//如果需要记住密码,则往外写cookied
c1.setMaxAge(10*24*60*60);
c2.setMaxAge(10*24*60*60);
}else{
//把没有过期cookie删除
c1.setMaxAge(0);
c2.setMaxAge(0);
}
response.addCookie(c1);
response.addCookie(c2);
}
}
return returnObject;
}
WorkbenchIndexController.java
@RequestMapping("/workbench/index.do")
public String index(){
//跳转到业务主页面
return "workbench/index";
}
前端页面
笔记4:同步请求VS异步请求
同步请求:浏览器窗口发出的请求,响应信息返回到浏览器窗口,所以会进行全局刷新。
异步请求:ajax发出的请求,响应信息返回到ajax的回调函数,既可以进行全局刷新,也可以进行局部刷新。小结: 如果需要进行全局刷新,推荐使用同步请求,当然也可以使用异步请求;
如果需要进行局部刷新,只能使用异步请求;
如果既可能进行全局刷新,也可能进行局部刷新,也是只能使用异步请求。
笔记5:使用jquery获取指定元素的指定属性的值
选择器.attr(“属性名”);//用来获取那些值不是true/false的属性的值.
选择器.prop(“属性名”);//用来获取值是true/false的属性的值.例如checked,selected,readonly,disabled等。
笔记6:jquery事件函数的用法
选择器.click(function(){//给指定的元素添加事件
//js代码
});选择器.click();//在指定的元素上模拟发生一次事件
login.jsp
//给整个浏览器窗口添加键盘按下事件
$(window).keydown(function (e) {
//如果按的是回车键,则提交登录请求
if(e.keyCode==13){
$("#loginBtn").click();
}
});
//给"登录"按钮添加单击事件
$("#loginBtn").click(function () {
//收集参数
var loginAct=$.trim($("#loginAct").val());
var loginPwd=$.trim($("#loginPwd").val());
var isRemPwd=$("#isRemPwd").prop("checked");
//表单验证
if (loginAct=="") {
alert("用户名不能为空");
return;
}
if (loginPwd=="") {
alert("密码不能为空");
return;
}
//显示正在验证
// $("#msg").text("正在努力验证...");
//发送请求
$.ajax({
url:'settings/qx/user/login.do',
data:{
loginAct:loginAct,
loginPwd:loginPwd,
isRemPwd:isRemPwd
},
type:'post',
dataType:'json',
success:function (data) {
if (data.code=="1"){
//跳转到业务主页面
window.location.href="workbench/index.do";
}else {
//提示信息
$("#msg").text(data.message);
}
},
beforeSend:function () {//当ajax向后台发送请求之前,会自动执行本函数;
//该函数的返回值能够决定ajax是否真正向后台发送请求:
//如果该函数返回true,则ajax会真正向后台发送请求;否则,如果该函数返回false,则ajax放弃向后台发送请求。
$("#msg").text("正在努力验证....");
return true;
}
});
});
安全退出
需求
用户在任意的业务页面,点击"退出"按钮,弹出确认退出的模态窗口;用户在确认退出的模态窗口,点击"确定"按钮,完成安全退出的功能.
*安全退出,清空cookie,销毁session
*退出完成之后,跳转到首页
时序&流程
Controller层
UserController
@RequestMapping("/settings/qx/user/logout.do")
public String logout(HttpServletResponse response, HttpSession session){
//清空cookie
Cookie c1=new Cookie("loginAct","1");
c1.setMaxAge(0);
response.addCookie(c1);
Cookie c2=new Cookie("loginPwd","1");
c2.setMaxAge(0);
response.addCookie(c2);
//销毁session
session.invalidate();
//跳转到首页 重定向
return "redirect:/";
}
前端页面
index.jsp
给确定退出按钮加id选择器
<button type="button" class="btn btn-primary" data-dismiss="modal" id="logoutBtn">确定</button>
//给“确定”按钮添加单击事件
$("#logoutBtn").click(function () {
//发送同步请求
window.location.href="settings/qx/user/logout.do";
});
登录验证
笔记7:过滤器
1)过滤器:
a)implements Filter{
–init
–doFilter
–destroy
}
b)配置过滤器:web.xml
2)拦截器:
a)提供拦截器类:implements HandlerInterceptor{
–pre
–post
–after
}
b)配置拦截器:springmvc.xml
LoginInterceptor.java
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
//如果用户没有登录成功,则跳转到登录页面
HttpSession session=httpServletRequest.getSession();
User user=(User) session.getAttribute(Contants.SESSION_USER);
if(user==null){
httpServletResponse.sendRedirect(httpServletRequest.getContextPath());//重定向时,url必须加项目的名称
return false;
}
return true;
}
applicationContext-mvc.xml
<mvc:interceptors>
<mvc:interceptor>
<!--配置拦截的请求-->
<mvc:mapping path="/settings/**"/>
<mvc:mapping path="/workbench/**"/>
<!--配置排除拦截的请求(优先级高)-->
<mvc:exclude-mapping path="/settings/qx/user/toLogin.do"/>
<mvc:exclude-mapping path="/settings/qx/user/login.do"/>
<!--拦截器类-->
<bean class="com.yyp.crm.settings.web.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>