litemall源码阅读4.02 后台的登录验证与个人理解
核心文件是src/main/java/org/linlinjava/litemall/admin/web/AdminAuthController.java
进入文件,找到登录验证请求的/admin/auth/login映射。
这里面主要包含了shrio安全框架的知识。我在这里卡了很长时间。
另外:springboot开启跨域这个知识点比较固定,就没写日志。
@RestController
@RequestMapping("/admin/auth")
@Validated
public class AdminAuthController {
private final Log logger = LogFactory.getLog(AdminAuthController.class); //控制台打印日志类
@Autowired
private LitemallAdminService adminService; //获取管理员帐号,密码,头像等信息。
@Autowired
private LitemallRoleService roleService; //获取角色。
@Autowired
private LitemallPermissionService permissionService; //获取每个角色的权限。
@Autowired
private LogHelper logHelper; //关于LogHelper。其实就是操作数据库的一个service。
/*
* { username : value, password : value }
*/
@PostMapping("/login")
public Object login(@RequestBody String body, HttpServletRequest request) {
String username = JacksonUtil.parseString(body, "username");
String password = JacksonUtil.parseString(body, "password");
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
return ResponseUtil.badArgument();
}
Subject currentUser = SecurityUtils.getSubject();
try {
//构建对象进行密码验证,该函数执行会调用src/main/java/org/linlinjava/litemall/admin/shiro/AdminAuthorizingRealm.java
//中的doGetAuthenticationInfo函数。
currentUser.login(new UsernamePasswordToken(username, password));
} catch (UnknownAccountException uae) {
logHelper.logAuthFail("登录", "用户帐号或密码不正确");
return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号或密码不正确");
} catch (LockedAccountException lae) {
logHelper.logAuthFail("登录", "用户帐号已锁定不可用");
return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号已锁定不可用");
} catch (AuthenticationException ae) {
logHelper.logAuthFail("登录", "认证失败");
return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "认证失败");
}
//通过shrio获取当前用户信息。
currentUser = SecurityUtils.getSubject();
//刷新用户表信息。
LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();
admin.setLastLoginIp(IpUtil.getIpAddr(request));
admin.setLastLoginTime(LocalDateTime.now());
adminService.updateById(admin);
//写入数据库日志
logHelper.logAuthSucceed("登录");
// 将用户信息返回到前端后台。
Map<String, Object> adminInfo = new HashMap<String, Object>();
adminInfo.put("nickName", admin.getUsername());
adminInfo.put("avatar", admin.getAvatar());
//最重要的一点,将当前登录后的Session写回到前端后台,当前端再发起请求时,
//会调用src/main/java/org/linlinjava/litemall/admin/shiro/AdminWebSessionManager.java
//中的getSessionId获取前端通过http头发送过来的session,交由后台验证当前的登录状态和权限。
Map<Object, Object> result = new HashMap<Object, Object>();
result.put("token", currentUser.getSession().getId());
result.put("adminInfo", adminInfo);
return ResponseUtil.ok(result);
}
}
litemall前后端分离的总结。
这篇文章创建的比较早,但其实是在完成4.04后才补充完毕的。大体上也了解了整体上的思路。
在传统前后端不分离的项目中。session与http申请都在一个后台中,所以可以统一处理这些关系。
但分离后,会有跨域的问题,即浏览器在加载一个服务器的资源后,访问另一个服务器的资源。
我一直以为这个问题。。。没什么好解释的,但仔细想来,也没这么简单。
首先说下vue后台的理解。vue后台,只是提供给浏览器相应的html和js代码。浏览器的运行状态如何,
vue后台是一点也不知道,一点也不管的。我曾经有个问题,就是vuex保存java后台的session,会不会
不同用户导致混乱,想明白这个问题后答案就很明确了。vuex本身就是保存在浏览器的,每个浏览器
都有一个实例。肯定不会发生混乱。
那么第二个问题来了,既然vue相关代码全都是运行在浏览器的,所有http请求,包括登录都是全部发往java后台的。
session为什么会有失效的问题呢?
我搜了好久,但更多的是讲处理办法,而非本质原因。
个人感觉这篇文章讲的很好。
首先我们先了解http是无状态协议。压根就不知道一个申请过来,这个申请的发起者
有没有登录过。所以有了cookie与session。
cookie是保存在浏览器端的。session是保存在服务器端的。
当一个浏览器发起连接,服务器创建一个session。该浏览器登录成功,服务器在session里设置一个属性标识该用户登录。
之后,将sessionID返回给客户,保存在浏览器中。当该浏览器再次发起请求,并且带着这个sessionID,服务器根据该ID检测,
该用户是否已经进行了登录,再进行后续操作。
那么回到session失效的问题。
我们登录页的后台地址实际上是vue后台,而登录的地址却是向java后台发申请。
这时候浏览器的同源策略就会阻止cookie的一些操作。因为这会带来风险,这篇文章有简单描述。
但是,我现在并不详细地了解这边是怎么具体操作的,是阻止java后台向当前vue后台的cookie里写内容。
或者阻止在向java后台发起http请求时,读内容。我想应该是前者。
但这边传统的session策略肯定是失效的。
回到litemall权限验证上来。当我们想java后台发出登录请求,java后台返回session后,我们
直接将session写入到了vuex中。当再向java后台发出http请求,从vuex中读取该session代替浏览器默认的session。
这也是为什么java后台要重写src/main/java/org/linlinjava/litemall/admin/shiro/AdminWebSessionManager.java。
就是在读取session是,不读取默认的sessionID,而是使用http请求头中的X-Litemall-Admin-Token代替了session。
到这里登录验证应该差不多写完了。
我在读登录验证以前,一直以为权限验证是登录时直接把权限返回的,现在看来并不是。接下来阅读权限验证的源码。