热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Shiro系列之的登录验证功能实现

目前在企业级项目里做权限安全方面喜欢使用Apache开源的Shiro框架或者Spring框架的子框架SpringSecurity。ApacheShiro是一个强大且易用的Java

目前在企业级项目里做权限安全方面喜欢使用Apache开源的Shiro框架或者Spring框架的子框架Spring Security。

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。

Shiro框架具有轻便,开源的优点,所以本博客介绍基于Shiro的登录验证实现。

在maven里加入shiro需要的jar

org.apache.shiroshiro-all1.2.3

在web.xml加上Shiro过滤器配置:

shiroFilterorg.springframework.web.filter.DelegatingFilterProxytargetFilterLifecycletrueshiroFilter/*

编写shiro的ShiroRealm类:

package org.muses.jeeplatform.core.security.shiro;import javax.annotation.Resource;import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.muses.jeeplatform.model.entity.User;
import org.muses.jeeplatform.service.UserService;/*** @description 基于Shiro框架的权限安全认证和授权* @author Nicky* @date 2017年3月12日*/
public class ShiroRealm extends AuthorizingRealm {/**注解引入业务类**/@ResourceUserService userService;/*** 登录信息和用户验证信息验证(non-Javadoc)* @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(AuthenticationToken)*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username = (String)token.getPrincipal(); //得到用户名 String password = new String((char[])token.getCredentials()); //得到密码User user = userService.findByUsername(username);/**检测是否有此用户 **/if(user == null){throw new UnknownAccountException();//没有找到账号异常}/**检验账号是否被锁定 **/if(Boolean.TRUE.equals(user.getLocked())){throw new LockedAccountException();//抛出账号锁定异常}/**AuthenticatingRealm使用CredentialsMatcher进行密码匹配**/if(null != username && null != password){return new SimpleAuthenticationInfo(username, password, getName());}else{return null;}}/*** 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc)* @see AuthorizingRealm#doGetAuthorizationInfo(PrincipalCollection)*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {String username = (String)pc.getPrimaryPrincipal();SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();authorizationInfo.setRoles(userService.getRoles(username));authorizationInfo.setStringPermissions(userService.getPermissions(username));System.out.println("Shiro授权");return authorizationInfo;}@Overridepublic void clearCachedAuthorizationInfo(PrincipalCollection principals) {super.clearCachedAuthorizationInfo(principals);}@Overridepublic void clearCachedAuthenticationInfo(PrincipalCollection principals) {super.clearCachedAuthenticationInfo(principals);}@Overridepublic void clearCache(PrincipalCollection principals) {super.clearCache(principals);}}

在Spring框架里集成Shiro,加入配置

/static/** = anon/upload/** = anon/plugins/** = anon/code = anon/login = anon/logincheck = anon/** = authc

登录验证控制类实现:

package org.muses.jeeplatform.web.controller;import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;import javax.servlet.http.HttpServletRequest;import net.sf.json.JSONArray;
import net.sf.json.JSONObject;import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.muses.jeeplatform.core.Constants;
import org.muses.jeeplatform.model.entity.Menu;
import org.muses.jeeplatform.model.entity.Permission;
import org.muses.jeeplatform.model.entity.Role;
import org.muses.jeeplatform.model.entity.User;
import org.muses.jeeplatform.service.MenuService;
import org.muses.jeeplatform.service.UserService;
import org.muses.jeeplatform.utils.Tools;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;/*** @description 登录操作的控制类,使用Shiro框架,做好了登录的权限安全认证,* getRemortIP()方法获取用户登录时的ip并保存到数据库* @author Nicky* @date 2017年3月15日*/
@Controller
public class LoginController extends BaseController {@AutowiredUserService userService;@AutowiredMenuService menuService;/*** 获取登录用户的IP* @throws Exception */public void getRemortIP(String username) { HttpServletRequest request = this.getRequest();Map map = new HashMap();String ip = "";if (request.getHeader("x-forwarded-for") == null) { ip = request.getRemoteAddr(); }else{ip = request.getHeader("x-forwarded-for"); }map.put("username", username);map.put("loginIp", ip);userService.saveIP(map);} /*** 访问后台登录页面* @return* @throws Exception*/@RequestMapping(value="/login",produces="text/html;charset=UTF-8")public ModelAndView toLogin()throws ClassNotFoundException{ModelAndView mv = this.getModelAndView();mv.setViewName("admin/frame/login");return mv;}/*** 基于Shiro框架的登录验证,页面发送JSON请求数据,* 服务端进行登录验证之后,返回Json响应数据,"success"表示验证成功* @param request* @return* @throws Exception*/@RequestMapping(value="/logincheck", produces="application/json;charset=UTF-8")@ResponseBodypublic String loginCheck(HttpServletRequest request)throws AuthenticationException{JSONObject obj = new JSONObject();String errInfo = "";//错误信息String logindata[] = request.getParameter("LOGINDATA").split(",");if(logindata != null && logindata.length == 3){//获取Shiro管理的SessionSubject subject = SecurityUtils.getSubject();Session session = subject.getSession();String codeSession = (String)session.getAttribute(Constants.SESSION_SECURITY_CODE);String code = logindata[2]; /**检测页面验证码是否为空,调用工具类检测**/if(Tools.isEmpty(code)){errInfo = "nullcode";}else{String username = logindata[0];String password = logindata[1];if(Tools.isNotEmpty(codeSession) && codeSession.equalsIgnoreCase(code)){//Shiro框架SHA加密String passwordsha = new SimpleHash("SHA-1",username,password).toString();System.out.println(passwordsha);//检测用户名和密码是否正确User user = userService.doLoginCheck(username,passwordsha);if(user != null){if(Boolean.TRUE.equals(user.getLocked())){errInfo = "locked";}else{//Shiro添加会话session.setAttribute("username", username);session.setAttribute(Constants.SESSION_USER, user);//删除验证码Sessionsession.removeAttribute(Constants.SESSION_SECURITY_CODE);//保存登录IPgetRemortIP(username);/**Shiro加入身份验证**/Subject sub = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(username,password);sub.login(token);}}else{//账号或者密码错误errInfo = "uerror";}if(Tools.isEmpty(errInfo)){errInfo = "success";}}else{//缺少参数errInfo="codeerror";}}}obj.put("result", errInfo);return obj.toString();}/*** 后台管理系统主页* @return* @throws Exception*/@RequestMapping(value="/admin/index")public ModelAndView toMain() throws AuthenticationException{ModelAndView mv = this.getModelAndView();/**获取Shiro管理的Session**/Subject subject = SecurityUtils.getSubject();Session session = subject.getSession();User user = (User)session.getAttribute(Constants.SESSION_USER);if(user != null){...//业务实现}else{//会话失效,返回登录界面mv.setViewName("admin/frame/login");}mv.setViewName("admin/frame/index");return mv;}/*** 注销登录* @return*/@RequestMapping(value="/logout")public ModelAndView logout(){ModelAndView mv = this.getModelAndView();/**Shiro管理Session**/Subject sub = SecurityUtils.getSubject();Session session = sub.getSession();session.removeAttribute(Constants.SESSION_USER);session.removeAttribute(Constants.SESSION_SECURITY_CODE);/**Shiro销毁登录**/Subject subject = SecurityUtils.getSubject();subject.logout();/**返回后台系统登录界面**/mv.setViewName("admin/frame/login");return mv;}}

前端Ajax和JQeury校验实现:

/**客户端校验**/function checkValidity() {if ($("#username").val() == "") {$("#username").tips({side : 2,msg : '用户名不得为空',bg : '#AE81FF',time : 3});$("#username").focus();return false;}if ($("#password").val() == "") {$("#password").tips({side : 2,msg : '密码不得为空',bg : '#AE81FF',time : 3});$("#password").focus();return false;}if ($("#code").val() == "") {$("#code").tips({side : 1,msg : '验证码不得为空',bg : '#AE81FF',time : 3});$("#code").focus();return false;}return true;}/**服务器校验**/function loginCheck(){if(checkValidity()){var username = $("#username").val();var password = $("#password").val();var code = username+","+password+","+$("#code").val();$.ajax({type: "POST",//请求方式为POSTurl: 'logincheck',//检验urldata: {LOGINDATA:code,tm:new Date().getTime()},//请求数据dataType:'json',//数据类型为JSON类型cache: false,//关闭缓存success: function(data){//响应成功if("success" == data.result){$("#login").tips({side : 1,msg : '正在登录 , 请稍后 ...',bg : '#68B500',time : 10});window.location.href="admin/index";}else if("uerror" == data.result){$("#username").tips({side : 1,msg : "用户名或密码有误",bg : '#FF5080',time : 15});$("#username").focus();}else if("codeerror" == data.result){$("#code").tips({side : 1,msg : "验证码输入有误",bg : '#FF5080',time : 15});$("#code").focus();}else if("locked" == data.result){alert('您的账号被锁定了,呜呜');}else{$("#username").tips({side : 1,msg : "缺少参数",bg : '#FF5080',time : 15});$("#username").focus();}}});}}

这里写图片描述

登录成功,Session会话过期,需要重新登录,保证系统安全性
这里写图片描述

本博客只提供基于Shiro的登录验证实现,具体代码可以去我的github下载:https://github.com/u014427391/jeeplatform
欢迎star


推荐阅读
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 标题: ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文介绍了如何使用JSONObiect和Gson相关方法实现json数据与kotlin对象的相互转换。首先解释了JSON的概念和数据格式,然后详细介绍了相关API,包括JSONObject和Gson的使用方法。接着讲解了如何将json格式的字符串转换为kotlin对象或List,以及如何将kotlin对象转换为json字符串。最后提到了使用Map封装json对象的特殊情况。文章还对JSON和XML进行了比较,指出了JSON的优势和缺点。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • iOS超签签名服务器搭建及其优劣势
    本文介绍了搭建iOS超签签名服务器的原因和优势,包括不掉签、用户可以直接安装不需要信任、体验好等。同时也提到了超签的劣势,即一个证书只能安装100个,成本较高。文章还详细介绍了超签的实现原理,包括用户请求服务器安装mobileconfig文件、服务器调用苹果接口添加udid等步骤。最后,还提到了生成mobileconfig文件和导出AppleWorldwideDeveloperRelationsCertificationAuthority证书的方法。 ... [详细]
  • 本文讨论了如何使用Web.Config进行自定义配置节的配置转换。作者提到,他将msbuild设置为详细模式,但转换却忽略了带有替换转换的自定义部分的存在。 ... [详细]
author-avatar
卡布基诺2502934121
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有