这篇文章主要讲解了“怎么用Java整合Shiro实现用户登录认证功能”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用Java整合Shiro实现用户登录认证功能”吧!
让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:域名注册、雅安服务器托管、营销软件、网站建设、新洲网站维护、网站推广。
内容:
对于Shiro,相信各位小伙伴应该听说过,甚至应该也使用过!简单而言,它是一个很好用的用户身份认证、权限授权框架,可以实现用户登录认证,权限、资源授权、会话管理等功能,在本秒杀系统中,我们将主要采用该框架实现对用户身份的认证和用户的登录功能。
值得一提的是,本篇博文介绍的“Shiro实现用户登录认证”功能模块涉及到的数据库表为用户信息表user,下面进入代码实战环节。
org.apache.shiro shiro-ehcache ${shiro.version} org.apache.shiro shiro-core ${shiro.version} org.apache.shiro shiro-web ${shiro.version} org.apache.shiro shiro-spring ${shiro.version}
(2)紧接着是在UserController控制器中开发用户前往登录、用户登录以及用户退出登录的请求对应的功能方法,其完整的源代码如下所示:
@Autowired private Environment env; //跳到登录页 @RequestMapping(value = {"/to/login","/unauth"}) public String toLogin(){ return "login"; } //登录认证 @RequestMapping(value = "/login",method = RequestMethod.POST) public String login(@RequestParam String userName, @RequestParam String password, ModelMap modelMap){ String errorMsg=""; try { if (!SecurityUtils.getSubject().isAuthenticated()){ String newPsd=new Md5Hash(password,env.getProperty("shiro.encrypt.password.salt")).toString(); UsernamePasswordToken token=new UsernamePasswordToken(userName,newPsd); SecurityUtils.getSubject().login(token); } }catch (UnknownAccountException e){ errorMsg=e.getMessage(); modelMap.addAttribute("userName",userName); }catch (DisabledAccountException e){ errorMsg=e.getMessage(); modelMap.addAttribute("userName",userName); }catch (IncorrectCredentialsException e){ errorMsg=e.getMessage(); modelMap.addAttribute("userName",userName); }catch (Exception e){ errorMsg="用户登录异常,请联系管理员!"; e.printStackTrace(); } if (StringUtils.isBlank(errorMsg)){ return "redirect:/index"; }else{ modelMap.addAttribute("errorMsg",errorMsg); return "login"; } } //退出登录 @RequestMapping(value = "/logout") public String logout(){ SecurityUtils.getSubject().logout(); return "login"; }
其中,在匹配用户的密码时,我们在这里采用的Md5Hash的方法,即MD5加密的方式进行匹配(因为数据库的user表中用户的密码字段存储的正是采用MD5加密后的加密串)
前端页面login.jsp的内容比较简单,只需要用户输入最基本的用户名和密码即可,如下图所示为该页面的部分核心源代码:
当前端提交“用户登录”请求时,将以“提交表单”的形式将用户名、密码提交到后端UserController控制器对应的登录方法中,该方法首先会进行最基本的参数判断与校验,校验通过之后,会调用Shiro内置的组件SecurityUtils.getSubject().login()方法执行登录操作,其中的登录操作将主要在 “自定义的Realm的doGetAuthenticationInfo方法”中执行。
(3)接下来是基于Shiro的AuthorizingRealm,开发自定义的Realm,并实现其中的用户登录认证方法,即doGetAuthenticationInfo()方法。其完整的源代码如下所示:
/** * 用户自定义的realm-用于shiro的认证、授权 * @Author:debug (SteadyJack) * @Date: 2019/7/2 17:55 **/ public class CustomRealm extends AuthorizingRealm{ private static final Logger log= LoggerFactory.getLogger(CustomRealm.class); private static final Long sessionKeyTimeOut=3600_000L; @Autowired private UserMapper userMapper; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } //认证-登录 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken; String userName=token.getUsername(); String password=String.valueOf(token.getPassword()); log.info("当前登录的用户名={} 密码={} ",userName,password); User user=userMapper.selectByUserName(userName); if (user==null){ throw new UnknownAccountException("用户名不存在!"); } if (!Objects.equals(1,user.getIsActive().intValue())){ throw new DisabledAccountException("当前用户已被禁用!"); } if (!user.getPassword().equals(password)){ throw new IncorrectCredentialsException("用户名密码不匹配!"); } SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user.getUserName(),password,getName()); setSession("uid",user.getId()); return info; } /** * 将key与对应的value塞入shiro的session中-最终交给HttpSession进行管理(如果是分布式session配置,那么就是交给redis管理) * @param key * @param value */ private void setSession(String key,Object value){ Session session=SecurityUtils.getSubject().getSession(); if (session!=null){ session.setAttribute(key,value); session.setTimeout(sessionKeyTimeOut); } } }
其中,userMapper.selectByUserName(userName);主要用于根据userName查询用户实体信息,其对应的动态Sql的写法如下所示:
值得一提的是,当用户登录成功时(即用户名和密码的取值跟数据库的user表相匹配),我们会借助Shiro的Session会话机制将当前用户的信息存储至服务器会话中,并缓存一定时间!(在这里是3600s,即1个小时)!
(4)最后是我们需要实现“用户在访问待秒杀商品详情或者抢购商品或者任何需要进行拦截的业务请求时,如何自动检测用户是否处于登录的状态?如果已经登录,则直接进入业务请求对应的方法逻辑,否则,需要前往用户登录页要求用户进行登录”。
基于这样的需求,我们需要借助Shiro的组件ShiroFilterFactoryBean 实现“用户是否登录”的判断,以及借助FilterChainDefinitionMap拦截一些需要授权的链接URL,其完整的源代码如下所示:
/** * shiro的通用化配置 * @Author:debug (SteadyJack) * @Date: 2019/7/2 17:54 **/ @Configuration public class ShiroConfig { @Bean public CustomRealm customRealm(){ return new CustomRealm(); } @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager(); securityManager.setRealm(customRealm()); securityManager.setRememberMeManager(null); return securityManager; } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(){ ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean(); bean.setSecurityManager(securityManager()); bean.setLoginUrl("/to/login"); bean.setUnauthorizedUrl("/unauth"); //对于一些授权的链接URL进行拦截 MapfilterChainDefinitionMap=new HashMap<>(); filterChainDefinitionMap.put("/to/login","anon"); filterChainDefinitionMap.put("/**","anon"); filterChainDefinitionMap.put("/kill/execute","authc"); filterChainDefinitionMap.put("/item/detail/*","authc"); bean.setFilterChainDefinitionMap(filterChainDefinitionMap); return bean; } }
从上述该源代码中可以看出,Shiro的ShiroFilterFactoryBean组件将会对 URL=/kill/execute 和 URL=/item/detail/* 的链接进行拦截,即当用户访问这些URL时,系统会要求当前的用户进行登录(前提是用户还没登录的情况下!如果已经登录,则直接略过,进入实际的业务模块!)
除此之外,Shiro的ShiroFilterFactoryBean组件还设定了 “前往登录页”和“用户没授权/没登录的前提下的调整页”的链接,分别是 /to/login 和 /unauth!
(5)至此,整合Shiro框架实现用户的登录认证的前后端代码实战已经完毕了,将项目/系统运行在外置的tomcat服务器中,打开浏览器即可访问进入“待秒杀商品的列表页”,点击“详情”,此时,由于用户还没登陆,故而将跳转至用户登录页,如下图所示:
输入用户名:debug,密码:123456,点击“登录”按钮,即可登录成功,并成功进入“详情页”,如下图所示:
登录成功之后,再回到刚刚上一个列表页,即“待秒杀商品的列表页”,再次点击“详情”按钮,此时会直接进入“待秒杀商品的详情页”,而不会跳转至“用户登录页”,而且用户的登录态将会持续1个小时!(这是借助Shiro的Session的来实现的)。
感谢各位的阅读,以上就是“怎么用Java整合Shiro实现用户登录认证功能”的内容了,经过本文的学习后,相信大家对怎么用Java整合Shiro实现用户登录认证功能这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!