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


声明:本文转载自https://my.oschina.net/panzi1/blog/1579613,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

前言

    在上一篇博客中已经实现了单聊和群聊的功能,并且做了详细讲解。不过上次还缺少灵活性,就是当前用户信息的问题,本篇中 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

本文发表于2017年11月25日 10:33
(c)注:本文转载自https://my.oschina.net/panzi1/blog/1579613,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2534 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1