若依框架学习
- 一、01-登录流程
- 1、用户发送请求进入到登录页面
- 2、用户点击登录按钮
- 二、02-首页
一、01-登录流程
1、用户发送请求进入到登录页面
//SysLoginController.java
@GetMapping("/login")
public String login(HttpServletRequest request, HttpServletResponse response)
{
// 如果是Ajax请求,返回Json字符串。
if (ServletUtils.isAjaxRequest(request))
{
return ServletUtils.renderString(response, "{\"code\":\"1\",\"msg\":\"未登录或登录超时。请重新登录\"}");
}
return "login";
}
- 对ajax和视图返回进行区分
//ServletUtils.java
/**
* 是否是Ajax异步请求
*
* @param request
*/
public static boolean isAjaxRequest(HttpServletRequest request)
{
String accept = request.getHeader("accept");
if (accept != null && accept.indexOf("application/json") != -1)
{
return true;
}
String xRequestedWith = request.getHeader("X-Requested-With");
if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1)
{
return true;
}
String uri = request.getRequestURI();
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml"))
{
return true;
}
String ajax = request.getParameter("__ajax");
if (StringUtils.inStringIgnoreCase(ajax, "json", "xml"))
{
return true;
}
return false;
}
- 同时页面发送一个验证码请求
// SysCaptchaController.java
if ("math".equals(type))
{
String capText = captchaProducerMath.createText();
capStr = capText.substring(0, capText.lastIndexOf("@"));
code = capText.substring(capText.lastIndexOf("@") + 1);
bi = captchaProducerMath.createImage(capStr);
}
else if ("char".equals(type))
{
capStr = code = captchaProducer.createText();
bi = captchaProducer.createImage(capStr);
}
session.setAttribute(Constants.KAPTCHA_SESSION_KEY, code);
out = response.getOutputStream();
ImageIO.write(bi, "jpg", out);
out.flush();
- 若依使用的是谷歌提供的一个验证码生成器
com.google.code.kaptcha
,可以在配置类中配置验证码生成的参数,最后返回一个ImageIO
对象,controller
将图片对象通过流的形式写入到前端页面 - 验证码形式支持字符和数学运算,数学运算需要自定义一个文本生成器`
// 验证码文本生成器
properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.framework.config.KaptchaTextCreator");
2、用户点击登录按钮
- 前端发送ajax请求
//login.js
function login() {
$.modal.loading($("#btnSubmit").data("loading"));
var username = $.common.trim($("input[name='username']").val());
var password = $.common.trim($("input[name='password']").val());
var validateCode = $("input[name='validateCode']").val();
var rememberMe = $("input[name='rememberme']").is(':checked');
$.ajax({
type: "post",
url: ctx + "login",
data: {
"username": username,
"password": password,
"validateCode": validateCode, //请求体中的验证码会在shiro验证码过滤器中进行验证
"rememberMe": rememberMe
},
success: function(r) {
//响应数据成功后,根据响应代码跳转到不同的页面
if (r.code == web_status.SUCCESS) {
//登录成功,跳转到index页面
location.href = ctx + 'index';
} else {
//登录失败
//关闭loading蒙层
$.modal.closeLoading();
//模拟点击一下刷新验证码按钮
$('.imgcode').click();
//清空验证码输入值
$(".code").val("");
//显示响应的错误消息
$.modal.msg(r.msg);
}
}
});
}
- 发送POST请求到
/login
- 进行shiro权限验证
- 获取登录的token值
- 验证成功返回成功ajax数据,失败返回失败ajax数据
@PostMapping("/login")
@ResponseBody
public AjaxResult ajaxLogin(String username, String password, Boolean rememberMe)
{
UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);
Subject subject = SecurityUtils.getSubject();
try
{
subject.login(token);
return success();
}
catch (AuthenticationException e)
{
String msg = "用户或密码错误";
if (StringUtils.isNotEmpty(e.getMessage()))
{
msg = e.getMessage();
}
return error(msg);
}
}
二、02-首页
- 发送get请求index视图
ModelMap和Model的用法类似
-
SysUser user = ShiroUtils.getSysUser();
获取当前登录的用户信息
//SysIndexController.java
// 系统首页
@GetMapping("/index")
public String index(ModelMap mmap)
{
// 取身份信息
SysUser user = ShiroUtils.getSysUser();
// 根据用户id取出菜单
List<SysMenu> menus = menuService.selectMenusByUser(user);
mmap.put("menus", menus);
mmap.put("user", user);
mmap.put("sideTheme", configService.selectConfigByKey("sys.index.sideTheme"));
mmap.put("skinName", configService.selectConfigByKey("sys.index.skinName"));
mmap.put("ignoreFooter", configService.selectConfigByKey("sys.index.ignoreFooter"));
mmap.put("copyrightYear", RuoYiConfig.getCopyrightYear());
mmap.put("demoEnabled", RuoYiConfig.isDemoEnabled());
mmap.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate()));
mmap.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate()));
// 菜单导航显示风格
String menuStyle = configService.selectConfigByKey("sys.index.menuStyle");
// 移动端,默认使左侧导航菜单,否则取默认配置
String indexStyle = ServletUtils.checkAgentIsMobile(ServletUtils.getRequest().getHeader("User-Agent")) ? "index" : menuStyle;
// 优先Cookie配置导航菜单
Cookie[] cookies = ServletUtils.getRequest().getCookies();
for (Cookie cookie : cookies)
{
if (StringUtils.isNotEmpty(cookie.getName()) && "nav-style".equalsIgnoreCase(cookie.getName()))
{
indexStyle = cookie.getValue();
break;
}
}
String webIndex = "topnav".equalsIgnoreCase(indexStyle) ? "index-topnav" : "index";
return webIndex;
}
- 从数据库查询到的菜单列表是没有树形结构的,进行了二次封装,最终返回树形结构的菜单到视图
//SysMenuServiceImpl.java
/**
* 根据用户查询菜单
*
* @param user 用户信息
* @return 菜单列表
*/
@Override
public List<SysMenu> selectMenusByUser(SysUser user)
{
List<SysMenu> menus = new LinkedList<SysMenu>();
// 管理员显示所有菜单信息
if (user.isAdmin())
{
menus = menuMapper.selectMenuNormalAll();
}
else
{
menus = menuMapper.selectMenusByUserId(user.getUserId());
}
return getChildPerms(menus, 0);
}
/**
* 根据父节点的ID获取所有子节点
*
* @param list 分类表
* @param parentId 传入的父节点ID
* @return String
*/
public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId)
{
List<SysMenu> returnList = new ArrayList<SysMenu>();
for (Iterator<SysMenu> iterator = list.iterator(); iterator.hasNext();)
{
SysMenu t = (SysMenu) iterator.next();
// 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
if (t.getParentId() == parentId)
{
recursionFn(list, t);
returnList.add(t);
}
}
return returnList;
}
/**
* 递归列表
*
* @param list
* @param t
*/
private void recursionFn(List<SysMenu> list, SysMenu t)
{
// 得到子节点列表
List<SysMenu> childList = getChildList(list, t);
t.setChildren(childList);
for (SysMenu tChild : childList)
{
if (hasChild(list, tChild))
{
recursionFn(list, tChild);
}
}
}