您的位置:首页 > 编程语言 > Java开发

Spring boot + LayIM + t-io 使用thyemleaf模板和Shiro实现登录

2017-11-25 11:27 846 查看

前言

在上一篇博客中已经实现了单聊和群聊的功能,并且做了详细讲解。不过上次还缺少灵活性,就是当前用户信息的问题,本篇中 t-io 的部分较少。并且为了实现快速登录,thymeleaf和Shiro我也只是做了简单使用,不过就做个学习记录吧。

thymeleaf模板

网上的博客有很多,我就不在误人子弟了。简单的介绍下我使用的部分。首先我下载了基于layui的一个社区模板。下载地址:http://git.oschina.net/sentsin/fly,所以暂时就拿了他的布局和登录页面做登录功能。

下载的模板里面有个common文件夹,里面已经提取出了 footer.html 和 header.html.我们先在resources文件夹下新建templates文件夹。然后新建 header,footer两个模板文件。分别代码如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<header th:fragment="header">
<div class="header">
<!--具体的header的HTML-->
</div>
</header>
</body>
</html>

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<footer th:fragment="footer">
<div class="footer">
<!--具体的footer相关HTML-->
</div>
</footer>
</body>
</html>

然后在新建一个 layout 模板文件,引用上 header 和 footer。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.w3.org/1999/xhtml">
<head th:fragment="htmlhead">
<!--一些公共的JS,Css引用和meta等信息-->
</head>

<body>
<!--替换成header的内容-->
<div th:replace="fragments/header :: header">Header</div>
<!--针对具体页面的内容部分-->
<div layout:fragment="content" id="content" ></div>
<!--替换成footer的内容-->
<div th:replace="fragments/footer :: footer">footer</div>
</body>
</html>

比如,我把登录的页面内容套进去:

<!DOCTYPE html>
<!--layout:decorator="layout" 这句代码可不能丢,要不然就没法渲染content内容了-->
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.w3.org/1999/xhtml" layout:decorator="layout">
<head th:include="layout :: htmlhead" th:with="title='LayIM'"></head>
<body>
<scetion layout:fragment="content">
<div class="main layui-clear">

<!--登录窗体HTML-->

</div>
</scetion>
</body>
</html>

OK。页面布局就结束了,当然,我只是参照其他博客照猫画虎。目的就是快速搭建出架子,然后继续做核心部分。相关参考资料:https://www.cnblogs.com/ityouknow/p/5833560.html

运行效果如下:



Shiro

这个东东我也是用了很浅的一部分,不过在运行过程中,可以看到他已经做了很多授权,登录等相关的功能,这个我也就不多做介绍了。

相关参考资料:http://blog.csdn.net/ityouknow/article/details/73836159

关于登录步骤不多说,无非登录验证然后跳转到首页。在这我只是介绍一下我所遇到的坑:

//我 x,加上static 还不行,还得具体的路径。之前加上 /static/css/** 不可以,所以直接改为访问路径
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/images/**", "anon");
filterChainDefinitionMap.put("/html/**", "anon");

看我的注释就知道,刚开始我加了 static 路径下可以匿名访问,结果还是被拦截了,后来发现,需要写在浏览器中的路径(或者有其他原因?)

登录成功之后就会跳转到首页(index).如果登录不成功,配合 thymeleaf 语法显示不同的信息即可。这里呢我用了session存储错误信息,然后登录之后清空。登录部分代码如下:

/**
* 代码参考:http://www.ityouknow.com/springboot/2017/06/26/springboot-shiro.html
* */
@RequestMapping(value = "/login",method = RequestMethod.POST)
public String login(HttpServletRequest request) throws Exception{
request.getSession().removeAttribute("login_msg");
// 登录失败从request中获取shiro处理的异常信息。
// shiroLoginFailure:就是shiro异常类的全类名.
String exception = (String) request.getAttribute("shiroLoginFailure");
System.out.println("exception=" + exception);
String msg = "";
if (exception != null) {
if (UnknownAccountException.class.getName().equals(exception)) {
msg = "account";
} else if (IncorrectCredentialsException.class.getName().equals(exception)) {
msg = "pwd";
} else if ("kaptchaValidateFailed".equals(exception)) {
msg = "code";
} else {
msg = "other";
}
request.getSession().setAttribute("login_msg",msg);
}
// 此方法不处理登录成功,由shiro进行处理
return "/account/login";
}

然后前端用到了 th:if 语法,判断错误消息是否显示。

<form th:action="@{/account/login}" th:method="post">
<!--其他代码-->

<!--判断session中的login_msg是否==pwd-->
<div class="layui-form-mid"  th:if="${session.login_msg}=='pwd'">
<span style="color: #c00;">密码错误</span>
</div>

</form>

运行效果如下:





不细讲的原因是我自己用的比较粗浅,而且网上博客比较多,我这里只列举我用到了有坑的地方。或许坑也是我自己挖的。

登录后的业务处理

登录成功之后可以通过Shiro的Subject 拿到当前的用户ID,后来我以为在t-io服务端(LayimMsgHandler)也能拿到,后来发现too young too simple,因为他两个一个是端口8080,另外一个是端口8081,所以,Shiro的Session没有拿到。不过这个也有点类似SSO了, 后边打算用redis处理登录的用户ID问题。不过这里,我先用了生成token的方式,然后在连接服务器的时候,将token传过去,然后进行验证,在获取用户信息赋值给ContextUser,最后进行用户绑定和群组绑定。

/**
* 用 @Autowired 注解UserService 不起作用(原因我不是很清楚)
* 解析客户端的token,获取用户信息转化为ContextUser对象
* 将ContextUser对象,以UserId为Key,ContextUser为value存入ChannelContext的Attribute中,方便后续使用当前用户信息
* 获取用户群组,遍历调用 Aio.bindGroup方法加入群组
* */
private HttpResponse handleHandshakeUserInfo(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) {
UserService userService = (UserService) SpringUtil.getBean("userService");
//增加token验证方法

String path = httpRequest.getRequestLine().getPath();
String token = path.substring(1);

String userId = UserToken.IsValid(token);

if (userId == null) {
//没有token 未授权
httpResponse.setStatus(HttpResponseStatus.C401);
} else {
//解析token
LayimContextUserInfo userInfo = userService.getContextUserInfo(Long.parseLong(userId));
if (userInfo == null) {
//没有找到用户
httpResponse.setStatus(HttpResponseStatus.C404);
} else {
channelContext.setAttribute(userId, userInfo.getContextUser());
//绑定用户ID
Aio.bindUser(channelContext, userId);
//绑定用户群组
List<String> groupIds = userInfo.getGroupIds();
//绑定用户群信息
if (groupIds != null) {
groupIds.forEach((String groupId) -> Aio.bindGroup(channelContext, groupId));
}
}
}
return httpResponse;
}

UserToken.IsValid就是验证token的合法性。通过url传过来。所以,前端稍微改一下,还要在UserController中增加一个生成token的接口。那么连接websocket服务器的时候就要先获取token。

$.get('/layim/token',function (res) {
if(res.code>0) {
return;
}
socket.config({
log:true,
server:'ws://127.0.0.1:8888/'+res.data
});

//其他监听代码
})

所以在运行时候是酱紫的:



如果不加token或者token无效的话,会返回401



总结

本文内容零零散散的介绍了一些,主要是完成了登录的流程和服务端的对接。按理说,web端应该是和 websocket端分开的,只不过当时一心想合并到一起去。后来开发过程中发现完全是相当于两个项目之间的一些交互。当然这些都是我的个人理解。

在后续的博客中将会讲到LayIM的一些常用接口的使用和t-io的一些接口的探索使用。比如,用户在线情况,各种提示等。拜~

代码已经同步到:https://github.com/fanpan26/SpringBootLayIM
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring boot tio layim