springboot-shrio-mybatis登录验证与权限控制

来源:互联网 发布:刘备汉昭烈帝皮肤优化 编辑:程序博客网 时间:2024/05/18 06:25

springboot-shrio-mybatis

一、背景

最近做的一个spingboot项目中用到权限控制,网上也看了其他springboot集成shiro进行权限控制的文档。大多文档用户与角色为多对多关系,角色与权限多对多,我的项目需求用户与角色为单对单,角色与权限多对多。所以自己重新整理了表结构完成。

 

二、表结构

/*Navicat MySQL Data TransferSource Server         : 本地Source Server Version : 50528Source Host           : localhost:3306Source Database       : shiroTarget Server Type    : MYSQLTarget Server Version : 50528File Encoding         : 65001Date: 2017-09-14 16:41:39*/SET FOREIGN_KEY_CHECKS=0;-- ------------------------------ Table structure for `sys_permission`-- ----------------------------DROP TABLE IF EXISTS `sys_permission`;CREATE TABLE `sys_permission` (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `url` varchar(256) DEFAULT NULL COMMENT 'url地址',  `permission` varchar(64) DEFAULT NULL COMMENT '权限初始化',  `name` varchar(64) DEFAULT NULL COMMENT '描述',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;-- ------------------------------ Records of sys_permission-- ----------------------------INSERT INTO `sys_permission` VALUES ('1', '/static/**', 'anon', '静态资源');INSERT INTO `sys_permission` VALUES ('2', '/ajaxLogin', 'anon', 'ajax登录');INSERT INTO `sys_permission` VALUES ('3', '/logout', 'logout', '安全退出');INSERT INTO `sys_permission` VALUES ('4', '/*/*/upload', 'anon', '上传文件');INSERT INTO `sys_permission` VALUES ('5', '/admin/content', 'admin:content', '首页/系统管理');INSERT INTO `sys_permission` VALUES ('6', '/cms/content', 'cms:content', '内容管理');INSERT INTO `sys_permission` VALUES ('7', '/apps/content', 'apps:content', '首页');INSERT INTO `sys_permission` VALUES ('8', '/**', 'authc', '其他全部拦截');INSERT INTO `sys_permission` VALUES ('9', '/cms/article/add', 'article:add', '文章添加');-- ------------------------------ Table structure for `sys_role`-- ----------------------------DROP TABLE IF EXISTS `sys_role`;CREATE TABLE `sys_role` (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `name` varchar(32) DEFAULT NULL COMMENT '角色名称',  `create_by` varchar(64) DEFAULT NULL COMMENT '创建人',  `create_date` datetime DEFAULT NULL COMMENT '创建时间',  `update_by` varchar(64) DEFAULT NULL COMMENT '更新者',  `update_date` datetime DEFAULT NULL COMMENT '更新时间',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;-- ------------------------------ Records of sys_role-- ----------------------------INSERT INTO `sys_role` VALUES ('1', 'admin', '1', null, null, null);-- ------------------------------ Table structure for `sys_role_permission`-- ----------------------------DROP TABLE IF EXISTS `sys_role_permission`;CREATE TABLE `sys_role_permission` (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `rid` bigint(20) DEFAULT NULL COMMENT '角色ID',  `pid` bigint(20) DEFAULT NULL COMMENT '权限ID',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;-- ------------------------------ Records of sys_role_permission-- ----------------------------INSERT INTO `sys_role_permission` VALUES ('1', '1', '1');INSERT INTO `sys_role_permission` VALUES ('2', '1', '2');INSERT INTO `sys_role_permission` VALUES ('3', '1', '3');INSERT INTO `sys_role_permission` VALUES ('4', '1', '4');INSERT INTO `sys_role_permission` VALUES ('5', '1', '5');INSERT INTO `sys_role_permission` VALUES ('6', '1', '6');INSERT INTO `sys_role_permission` VALUES ('7', '1', '7');INSERT INTO `sys_role_permission` VALUES ('8', '1', '8');-- ------------------------------ Table structure for `sys_user`-- ----------------------------DROP TABLE IF EXISTS `sys_user`;CREATE TABLE `sys_user` (  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',  `no` varchar(100) DEFAULT NULL COMMENT '工号',  `password` varchar(100) DEFAULT NULL COMMENT '密码',  `name` varchar(100) DEFAULT NULL COMMENT '姓名',  `rid` bigint(20) DEFAULT NULL COMMENT '角色id',  `rname` varchar(100) DEFAULT NULL COMMENT '角色名称',  `email` varchar(200) DEFAULT NULL COMMENT '邮箱',  `mobile` varchar(200) DEFAULT NULL COMMENT '手机',  `login_ip` varchar(100) DEFAULT NULL COMMENT '当前登录ip',  `login_date` datetime DEFAULT NULL COMMENT '当前登录时间',  `last_login_ip` varchar(100) DEFAULT NULL COMMENT '最后登陆IP',  `last_login_date` datetime DEFAULT NULL COMMENT '最后登陆时间',  `create_by` varchar(64) DEFAULT NULL COMMENT '创建者',  `create_date` datetime DEFAULT NULL COMMENT '创建时间',  `update_by` varchar(64) DEFAULT NULL COMMENT '更新者',  `update_date` datetime DEFAULT NULL COMMENT '更新时间',  `status` bigint(1) DEFAULT '1' COMMENT '状态(1有效/0禁止登录)',  PRIMARY KEY (`id`),  KEY `sys_user_update_date` (`update_date`)) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8 COMMENT='用户表';-- ------------------------------ Records of sys_user-- ----------------------------INSERT INTO `sys_user` VALUES ('27', 'admin', '21232f297a57a5a743894a0e4a801fc3', 'admin', '1', 'admin', '11@11.com', '13111111111', null, null, '0:0:0:0:0:0:0:1', '2017-03-08 20:22:31', '2,超级管理员', '2015-11-06 14:01:01', '27,shen', '2017-03-08 20:22:31', '0');


三、添加pom依赖

<!-- shiro相关 -->        <dependency>            <groupId>org.apache.shiro</groupId>            <artifactId>shiro-spring</artifactId>            <version>1.2.5</version>        </dependency>        <dependency>            <groupId>org.apache.shiro</groupId>            <artifactId>shiro-ehcache</artifactId>            <version>1.2.5</version>        </dependency>

四、shiro配置文件

ShiroConfiguration.java

package com.jdy.conf;import org.apache.shiro.authc.credential.HashedCredentialsMatcher;import org.apache.shiro.cache.ehcache.EhCacheManager;import org.apache.shiro.spring.LifecycleBeanPostProcessor;import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.DependsOn;import java.util.LinkedHashMap;import java.util.Map;/** * shiro配置项  */@Configurationpublic class ShiroConfiguration {// 适用于Spring的Bean后处理器自动调用实现 或接口的Shiro对象上的init()和/或// destroy()方法。这种后处理器使得在Spring中更容易配置Shiro// bean,因为用户从不必担心是否必须指定init-method和destroy-method bean属性。@Bean(name = "lifecycleBeanPostProcessor")public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}// 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher// 指定加密方式方式,也可以在这里加入缓存,当用户超过五次登陆错误就锁定该用户禁止不断尝试登陆@Bean(name = "hashedCredentialsMatcher")public HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();credentialsMatcher.setHashAlgorithmName("MD5");credentialsMatcher.setHashIterations(1);credentialsMatcher.setStoredCredentialsHexEncoded(true);return credentialsMatcher;}//认证实现@Bean(name = "shiroRealm")@DependsOn("lifecycleBeanPostProcessor")public ShiroRealm shiroRealm() {ShiroRealm realm = new ShiroRealm();realm.setCredentialsMatcher(hashedCredentialsMatcher());return realm;}// 缓存@Bean(name = "ehCacheManager")@DependsOn("lifecycleBeanPostProcessor")public EhCacheManager ehCacheManager() {EhCacheManager ehCacheManager = new EhCacheManager();return ehCacheManager;}@Bean(name = "securityManager")public DefaultWebSecurityManager securityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(shiroRealm());securityManager.setCacheManager(ehCacheManager());// 用户授权/认证信息Cache,// 采用EhCache 缓存return securityManager;}@Bean(name = "shiroFilter")public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");Map<String, String> filterChainDefinitionManager = new LinkedHashMap<>();filterChainDefinitionManager.put("/logout", "logout");filterChainDefinitionManager.put("/index", "anon");filterChainDefinitionManager.put("/login", "anon");// anon 可以理解为不拦截filterChainDefinitionManager.put("/ajaxLogin", "anon");// anonfilterChainDefinitionManager.put("/apps/content", "user");filterChainDefinitionManager.put("/cms/content", "user");filterChainDefinitionManager.put("/sys/content", "user");filterChainDefinitionManager.put("/cms/article/add","perms[article:add]");filterChainDefinitionManager.put("/cms/article/edit.*","perms[article:edit]");// 可以理解为不拦截filterChainDefinitionManager.put("/static/**", "anon");// 静态资源不拦截filterChainDefinitionManager.put("/**", "authc");// 其他资源全部拦截(需登陆后才能查看)shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager);shiroFilterFactoryBean.setLoginUrl("/login");shiroFilterFactoryBean.setSuccessUrl("/");shiroFilterFactoryBean.setUnauthorizedUrl("/403");return shiroFilterFactoryBean;}@Bean@ConditionalOnMissingBeanpublic DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();daap.setProxyTargetClass(true);return daap;}@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();aasa.setSecurityManager(securityManager);return aasa;}}

ShiroRealm.java

package com.jdy.conf;import java.util.ArrayList;import java.util.HashSet;import java.util.List;import java.util.Set;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.SimpleAuthenticationInfo;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.authz.SimpleAuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.session.Session;import org.apache.shiro.subject.PrincipalCollection;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import com.jdy.sys.model.SysRolePermission;import com.jdy.sys.model.SysUser;import com.jdy.sys.service.SysPermissionService;import com.jdy.sys.service.SysRolePermissionService;import com.jdy.sys.service.SysUserService;/** * 获取用户的角色和权限信息  */public class ShiroRealm extends AuthorizingRealm {private Logger logger = LoggerFactory.getLogger(ShiroRealm.class);@Autowiredprivate SysUserService sysUserService;@Autowiredprivate SysRolePermissionService sysRolePermissionService;@Autowiredprivate SysPermissionService sysPermissionService;/** * 登录认证 * * @param authenticationToken * @return * @throws AuthenticationException */@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)throws AuthenticationException {UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;logger.info("验证当前Subject时获取到token为:" + token.toString());// 查出是否有此用户SysUser user = sysUserService.findByNo(token.getUsername());if (user != null) { Session session = SecurityUtils.getSubject().getSession(); session.setAttribute("user", user);//成功则放入session// 若存在,将此用户存放到登录认证info中,无需自己做密码对比,Shiro会为我们进行密码对比校验return new SimpleAuthenticationInfo(user.getNo(),user.getPassword(), getName());}return null;}/** * 权限认证 * * @param principalCollection * @return */@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {logger.info("##################执行Shiro权限认证##################");// 获取当前登录输入的用户名,等价于(String)String loginName = (String) super.getAvailablePrincipal(principalCollection);// 到数据库查是否有此对象SysUser user = sysUserService.findByNo(loginName);if (user != null) {// 权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 用户的角色集合Set<String> set = new HashSet<String>();set.add(user.getRname());info.setRoles(set);// 用户的权限集合List<SysRolePermission> srpList = sysRolePermissionService.selectByRid(user.getRid());List<String> pNameList = new ArrayList<String>();for (SysRolePermission sysRolePermission : srpList) {pNameList.add(sysPermissionService.selectByPrimaryKey(sysRolePermission.getPid()).getPermission());}info.addStringPermissions(pNameList);return info;}// 返回null的话,就会导致任何用户访问被拦截的请求时,都会自动跳转到unauthorizedUrl指定的地址return null;}}

五、LoginController


package com.jdy.sys.controller;import java.util.HashMap;import java.util.Map;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.ExcessiveAttemptsException;import org.apache.shiro.authc.IncorrectCredentialsException;import org.apache.shiro.authc.LockedAccountException;import org.apache.shiro.authc.UnknownAccountException;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.subject.Subject;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.validation.BindingResult;import org.springframework.web.bind.annotation.ModelAttribute;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.support.RedirectAttributes;import com.jdy.sys.model.SysUser;import com.jdy.sys.service.SysUserService;import com.jdy.sys.util.Constant;import com.jdy.sys.util.SysUserUtils;import com.jdy.util.StringUtil;/** * @author LiuBang * * 2017年9月8日 下午5:16:17 */@Controllerpublic class LoginController {@Resourceprivate SysUserService sysUserService;private static final Logger logger = LoggerFactory.getLogger(LoginController.class);/** * 403无权限页面 * @param model * @param request * @return */@RequestMapping(value="/403")public String toError(Model model, HttpServletRequest request) {return "403";}/** * 管理主页 *  * @param model * @param request * @return */@RequestMapping(value="/index")public String toIndex(Model model, HttpServletRequest request) {if( SysUserUtils.getSessionLoginUser() == null){return "login";}return "index";}/** * 跳转到登录页面 *  * @return */@RequestMapping(value = "login", method = RequestMethod.GET)public String toLogin() {if( SysUserUtils.getSessionLoginUser() != null ){return "redirect:/index";}return "login";}/** * 登录验证 *  * @param no  工号 * @param password  密码 * @return */@RequestMapping(value = "/login", method = RequestMethod.POST)public ModelAndView checkLogin(String no,String password) {ModelAndView mv=new ModelAndView();        String username = no;        UsernamePasswordToken token = new UsernamePasswordToken(no, password);        //获取当前的Subject          Subject currentUser = SecurityUtils.getSubject();          try {              //在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm执行必须的认证检查              //每个Realm都能在必要时对提交的AuthenticationTokens作出反应              //所以这一步在调用login(token)方法时,它会走到MyRealm.doGetAuthenticationInfo()方法中,具体验证方式详见此方法              logger.info("对用户[" + username + "]进行登录验证..验证开始");              currentUser.login(token);              logger.info("对用户[" + username + "]进行登录验证..验证通过");          }catch(UnknownAccountException uae){              logger.info("对用户[" + username + "]进行登录验证..验证未通过,未知账户");              mv.addObject("message", "未知账户");          }catch(IncorrectCredentialsException ice){              logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误的凭证");            mv.addObject("message", "密码不正确");          }catch(LockedAccountException lae){              logger.info("对用户[" + username + "]进行登录验证..验证未通过,账户已锁定");             mv.addObject("message", "账户已锁定");          }catch(ExcessiveAttemptsException eae){              logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误次数过多");              mv.addObject("message", "用户名或密码错误次数过多");        }catch(AuthenticationException ae){              //通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景              logger.info("对用户[" + username + "]进行登录验证..验证未通过,堆栈轨迹如下");              mv.addObject("message", "用户名或密码不正确");            ae.printStackTrace();          }          //验证是否登录成功          if(currentUser.isAuthenticated()){              logger.info("用户[" + username + "]登录认证通过(这里可以进行一些认证通过后的一些系统参数初始化操作)");            mv.setViewName("/index");        }else{              token.clear();              mv.setViewName("/login");        }          return mv;}/** * 用户退出 *  * @return 跳转到登录页面 */@RequestMapping("logout")public String logout(RedirectAttributes redirectAttributes) {        //使用权限管理工具进行用户的退出,跳出登录,给出提示信息        SecurityUtils.getSubject().logout();          redirectAttributes.addFlashAttribute("message", "您已安全退出");          return "redirect:/login";}}


原创粉丝点击