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

springboot-shrio-mybatis

一、背景

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

 

二、表结构

/*
Navicat MySQL Data Transfer

Source Server         : 本地
Source Server Version : 50528
Source Host           : localhost:3306
Source Database       : shiro

Target Server Type    : MYSQL
Target Server Version : 50528
File Encoding         : 65001

Date: 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配置项 
 */
@Configuration
public 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");// anon
		filterChainDefinitionManager.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
	@ConditionalOnMissingBean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
		daap.setProxyTargetClass(true);
		return daap;
	}

	@Bean
	public 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);

	@Autowired
	private SysUserService sysUserService;
	@Autowired
	private SysRolePermissionService sysRolePermissionService;
	@Autowired
	private SysPermissionService sysPermissionService;

	/**
	 * 登录认证
	 *
	 * @param authenticationToken
	 * @return
	 * @throws AuthenticationException
	 */
	@Override
	protected 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
	 */
	@Override
	protected 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
 */
@Controller
public class LoginController {

	@Resource
	private 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";
	}
	

}


  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值