权限表设计
/*
Navicat MySQL Data Transfer
Source Server : root
Source Server Version : 50642
Source Host : localhost:3306
Source Database : study
Target Server Type : MYSQL
Target Server Version : 50642
File Encoding : 65001
Date: 2019-02-27 11:37:01
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for customer
-- ----------------------------
DROP TABLE IF EXISTS `customer`;
CREATE TABLE `customer` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`customer_id` varchar(20) NOT NULL COMMENT '会员编号',
`customer_name` varchar(20) NOT NULL COMMENT '会员名称',
`password` varchar(32) DEFAULT NULL COMMENT '密码',
`phone_number` varchar(11) DEFAULT NULL COMMENT '手机号码',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
`status` enum('1','2','3') NOT NULL DEFAULT '1' COMMENT '状态:1-正常;2-冻结;3-不可用',
`create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `customer_id` (`customer_id`),
UNIQUE KEY `phone_number` (`phone_number`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='会员表';
-- ----------------------------
-- Records of customer
-- ----------------------------
INSERT INTO `customer` VALUES ('1', 'abc123456', 'abc', 'e10adc3949ba59abbe56e057f20f883e', '13412345678', 'abc@163.com', '1', '2019-02-21 10:16:35', '2019-02-21 10:16:35');
INSERT INTO `customer` VALUES ('2', 'asd123456', 'ssd', 'e10adc3949ba59abbe56e057f20f883e', '13412345679', 'asd@163.com', '1', '2019-02-21 10:16:35', '2019-02-21 10:16:35');
INSERT INTO `customer` VALUES ('3', 'qwe123456', 'qwe', 'e10adc3949ba59abbe56e057f20f883e', '13412345632', 'qwe@163.com', '1', '2019-02-21 10:16:35', '2019-02-21 10:16:35');
INSERT INTO `customer` VALUES ('4', 'zxc123456', 'zxc', 'e10adc3949ba59abbe56e057f20f883e', '13412345622', 'zxc@163.com', '1', '2019-02-21 10:16:35', '2019-02-21 10:16:35');
-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`parent_id` int(11) DEFAULT NULL COMMENT '父级权限id',
`name` varchar(20) NOT NULL COMMENT '权限名称',
`permission` varchar(50) NOT NULL COMMENT '权限字符串,如employees:create,employees:update,employees:delete',
`type` enum('0','1','2') NOT NULL DEFAULT '0' COMMENT '权限类型:0-目录;1-菜单;2-按钮',
`url` varchar(100) DEFAULT NULL COMMENT '资源路径',
`status` enum('0','1') NOT NULL DEFAULT '1' COMMENT '状态:0-不可用;1-可用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 COMMENT='权限表';
-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO `sys_permission` VALUES ('1', null, '会员管理', 'customer', '0', '/customer', '1');
INSERT INTO `sys_permission` VALUES ('2', '1', '会员列表', 'customer:list', '1', '/customer/list', '1');
INSERT INTO `sys_permission` VALUES ('3', '1', '会员等级', 'customer:level', '1', '/customer/level', '1');
INSERT INTO `sys_permission` VALUES ('4', '2', '查看会员', 'customer:view', '2', '/customer/view', '1');
INSERT INTO `sys_permission` VALUES ('5', '2', '添加会员', 'customer:add', '2', '/customer/add', '1');
INSERT INTO `sys_permission` VALUES ('6', '2', '修改会员', 'customer:update', '2', '/customer/update', '1');
INSERT INTO `sys_permission` VALUES ('7', '2', '删除会员', 'customer:delete', '2', '/customer/delete', '1');
INSERT INTO `sys_permission` VALUES ('8', null, '商品管理', 'product', '0', '/product', '1');
INSERT INTO `sys_permission` VALUES ('9', '8', '商品列表', 'product:list', '1', '/product/list', '1');
INSERT INTO `sys_permission` VALUES ('10', '8', '商品分类', 'product:category', '1', '/product/category', '1');
INSERT INTO `sys_permission` VALUES ('11', '8', '商品回收站', 'product:recycle', '1', '/product/recycle', '1');
INSERT INTO `sys_permission` VALUES ('12', '9', '添加商品', 'product:add', '2', '/product/add', '1');
INSERT INTO `sys_permission` VALUES ('13', '9', '修改商品', 'product:update', '2', '/product/update', '1');
INSERT INTO `sys_permission` VALUES ('14', '9', '删除商品', 'product:delete', '2', '/product/delete', '1');
INSERT INTO `sys_permission` VALUES ('15', null, '订单管理', 'order', '0', '/order', '1');
INSERT INTO `sys_permission` VALUES ('16', '15', '订单列表', 'order:list', '1', '/order/list', '1');
INSERT INTO `sys_permission` VALUES ('17', '16', '查看订单', 'order:view', '2', '/order/view', '1');
INSERT INTO `sys_permission` VALUES ('18', '16', '修改订单', 'order:update', '2', '/order/update', '1');
INSERT INTO `sys_permission` VALUES ('19', '16', '删除订单', 'order:delete', '2', '/order/delete', '1');
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`role` varchar(20) NOT NULL COMMENT '角色名称',
`description` varchar(255) DEFAULT NULL COMMENT '角色描述',
`status` enum('0','1') NOT NULL DEFAULT '1' COMMENT '状态:0-不可用;1-可用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='角色表';
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', 'manager', '管理员', '1');
INSERT INTO `sys_role` VALUES ('2', 'producter', '生产员', '1');
INSERT INTO `sys_role` VALUES ('3', 'salesman', '销售员', '1');
-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission` (
`role_id` int(11) NOT NULL COMMENT '角色表主键',
`permission_id` int(11) NOT NULL COMMENT '权限表主键'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色权限表';
-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
INSERT INTO `sys_role_permission` VALUES ('1', '1');
INSERT INTO `sys_role_permission` VALUES ('1', '2');
INSERT INTO `sys_role_permission` VALUES ('1', '3');
INSERT INTO `sys_role_permission` VALUES ('1', '4');
INSERT INTO `sys_role_permission` VALUES ('1', '5');
INSERT INTO `sys_role_permission` VALUES ('1', '6');
INSERT INTO `sys_role_permission` VALUES ('1', '7');
INSERT INTO `sys_role_permission` VALUES ('1', '8');
INSERT INTO `sys_role_permission` VALUES ('1', '9');
INSERT INTO `sys_role_permission` VALUES ('1', '10');
INSERT INTO `sys_role_permission` VALUES ('1', '11');
INSERT INTO `sys_role_permission` VALUES ('1', '12');
INSERT INTO `sys_role_permission` VALUES ('1', '13');
INSERT INTO `sys_role_permission` VALUES ('1', '14');
INSERT INTO `sys_role_permission` VALUES ('1', '15');
INSERT INTO `sys_role_permission` VALUES ('1', '16');
INSERT INTO `sys_role_permission` VALUES ('1', '17');
INSERT INTO `sys_role_permission` VALUES ('1', '18');
INSERT INTO `sys_role_permission` VALUES ('1', '19');
INSERT INTO `sys_role_permission` VALUES ('2', '8');
INSERT INTO `sys_role_permission` VALUES ('2', '9');
INSERT INTO `sys_role_permission` VALUES ('2', '10');
INSERT INTO `sys_role_permission` VALUES ('2', '11');
INSERT INTO `sys_role_permission` VALUES ('2', '12');
INSERT INTO `sys_role_permission` VALUES ('2', '13');
INSERT INTO `sys_role_permission` VALUES ('2', '14');
INSERT INTO `sys_role_permission` VALUES ('3', '15');
INSERT INTO `sys_role_permission` VALUES ('3', '16');
INSERT INTO `sys_role_permission` VALUES ('3', '17');
INSERT INTO `sys_role_permission` VALUES ('3', '18');
INSERT INTO `sys_role_permission` VALUES ('3', '19');
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` varchar(20) NOT NULL COMMENT '用户名',
`password` varchar(32) NOT NULL COMMENT '密码',
`salt` varchar(32) DEFAULT NULL COMMENT '加密盐值',
`status` enum('1','2','3') NOT NULL DEFAULT '1' COMMENT '状态:1-正常;2-冻结;3-不可用',
`create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='用户表';
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', 'f6a93fb80dccf33119610712eb41a3cb', 'admin#123456', '1', '2019-02-19 03:02:27', '2019-02-20 17:56:56');
INSERT INTO `sys_user` VALUES ('2', 'product', '47d7920d1af0a48c3ad9a4897009bd5c', 'product#123456', '1', '2019-02-19 03:02:27', '2019-02-20 18:00:25');
INSERT INTO `sys_user` VALUES ('3', 'sale', '866ff773736059bbbd48e89676712c59', 'sale#123456', '1', '2019-02-20 17:59:49', '2019-02-20 17:59:49');
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`user_id` int(11) NOT NULL COMMENT '用户表主键',
`role_id` int(11) NOT NULL COMMENT '角色表主键'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户角色表';
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '1');
INSERT INTO `sys_user_role` VALUES ('2', '2');
INSERT INTO `sys_user_role` VALUES ('3', '3');
导入依赖
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
创建实体类、Mapper接口、Service类
这里我使用的是
mybatis-plus逆向生成工具,具体可以看源码
创建自定义安全数据源Realm
package com.lx.springboot.config;
import com.lx.springboot.entity.SysPermission;
import com.lx.springboot.entity.SysRole;
import com.lx.springboot.entity.SysUser;
import com.lx.springboot.service.SysPermissionService;
import com.lx.springboot.service.SysRoleService;
import com.lx.springboot.service.SysUserService;
import org.apache.shiro.authc.*;
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.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @ClassName ShiroRealm
* @Description 自定义安全数据源
*/
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private SysUserService sysUserService;
@Autowired
private SysRoleService sysRoleService;
@Autowired
private SysPermissionService sysPermissionService;
/**
* 授权
*
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//获取已经认证通过的用户信息
SysUser sysUser = (SysUser) principals.getPrimaryPrincipal();
//根据用户id查询角色信息
List<SysRole> sysRoles = sysRoleService.getSysRoleByUserId(sysUser.getId());
//函数式编程+lambda表达式
Set<String> roles = sysRoles.stream().map(SysRole::getRole).collect(Collectors.toSet());
authorizationInfo.setRoles(roles);
//根据用户id查询权限信息
List<SysPermission> sysPermissions = sysPermissionService.getSysPermissionByUserId(sysUser.getId());
Set<String> permissions = sysPermissions.stream().map(SysPermission::getPermission).collect(Collectors.toSet());
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
/**
* 认证
*
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取用户名
String username = (String) token.getPrincipal();
//根据用户名查询用户信息
SysUser sysUser = sysUserService.getSysUserByUsername(username);
//判断用户是否存在
if (Objects.isNull(sysUser)) {
throw new UnknownAccountException();
}
//判断用户状态是否正常
if (Objects.equals("2", sysUser.getStatus()) || Objects.equals("3", sysUser.getStatus())) {
throw new DisabledAccountException();
}
return new SimpleAuthenticationInfo(sysUser, sysUser.getPassword(), ByteSource.Util.bytes(sysUser.getSalt()), getName());
}
}
创建SpringBoot整合Shiro配置类
package com.lx.springbootshiro.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @Author: 水越帆
* QQ:1548353431
*/
@Configuration
public class ShiroConfig {
/**
* 配置拦截器
*
* 定义拦截URL权限,优先级从上到下
* 1). anon : 匿名访问,无需登录
* 2). authc : 登录后才能访问
* 3). logout: 登出
* 4). roles : 角色过滤器
*
* URL 匹配风格
* 1). ?:匹配一个字符,如 /admin? 将匹配 /admin1,但不匹配 /admin 或 /admin/;
* 2). *:匹配零个或多个字符串,如 /admin* 将匹配 /admin 或/admin123,但不匹配 /admin/1;
* 2). **:匹配路径中的零个或多个路径,如 /admin/** 将匹配 /admin/a 或 /admin/a/b
*
* 配置身份验证成功,失败的跳转路径
*
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shirFilter(DefaultWebSecurityManager securityManager)
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
filterChainDefinitionMap.put("/static/**", "anon"); // 静态资源匿名访问
filterChainDefinitionMap.put("/bootstrap/**", "anon"); // 静态资源匿名访问
filterChainDefinitionMap.put("/css/**", "anon"); // 静态资源匿名访问
filterChainDefinitionMap.put("/js/**", "anon"); // 静态资源匿名访问
filterChainDefinitionMap.put("/img/**", "anon"); // 静态资源匿名访问
filterChainDefinitionMap.put("/user/login", "anon");// 登录匿名访问
filterChainDefinitionMap.put("/user/doLogin", "anon");// 登录匿名访问
filterChainDefinitionMap.put("/user/logout", "logout"); // 用户退出,只需配置logout即可实现该功能
filterChainDefinitionMap.put("/**", "authc"); // 其他路径均需要身份认证,一般位于最下面,优先级最低
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
shiroFilterFactoryBean.setLoginUrl("/user/login"); // 登录的路径
shiroFilterFactoryBean.setSuccessUrl("/index"); // 登录成功后跳转的路径
shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 验证失败后跳转的路径
return shiroFilterFactoryBean;
}
/**
* SecurityManager 安全管理器;Shiro的核心
*/
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm());
return securityManager;
}
/**
* 自定义Realm,可以多个
*/
@Bean
public ShiroRealm shiroRealm() {
ShiroRealm shiroRealm = new ShiroRealm();
shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return shiroRealm;
}
/**
* 配置Shiro生命周期处理器
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
/**
* 自动创建代理类,若不添加,Shiro的注解可能不会生效。
*/
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new
DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启Shiro的注解
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new
AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
/**
* 凭证匹配器
*
* (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 )
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new
HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
hashedCredentialsMatcher.setHashIterations(2);
return hashedCredentialsMatcher;
}
/**
* Shiro方言,支持Thymeleaf中使用shiro标签
*/
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
登录认证
package com.lx.springboot.controller;
import com.lx.springboot.entity.SysUser;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
/**
* <p>
* 用户表 前端控制器
* </p>
*/
@Controller
public class SysUserController {
/**
* 跳转到登陆页面
*
* @return
*/
@RequestMapping({"", "/user/login"})
public String login(){
return "login";
}
/**
* 登陆
*
* @param user
* @return
*/
@PostMapping("/user/doLogin")
public String doLogin(SysUser user, HttpSession session){
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
SecurityUtils.getSubject().login(token);
//用户名保存到session中
session.setAttribute("username", user.getUsername());
return "redirect:/index";
}
/**
* 退出
*
* @return
*/
@RequestMapping("/user/logout")
public String logout(){
SecurityUtils.getSubject().logout();
return "redirect:/user/login";
}
}
使用Shiro注解授权
@Controller
@RequestMapping("/users")
public class UserController {
@GetMapping("")
@RequiresPermissions({ "user" })
public String userList() {
return "/user/user";
} }
在Thymeleaf中使用Shiro标签
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
使用shiro标签
<span shiro:authenticated="true" > <span>欢迎您:<span th:text="${userInfo.realName}"></span></span> </span>
权限验证失败统一处理
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理认证失败异常
*
* @param e
* @param model
* @return
*/
@ExceptionHandler(AuthenticationException.class)
public String authenticationExceptionProcessing(AuthenticationException e, Model model) {
String msg = null;
if (e instanceof DisabledAccountException) {
msg = "账户异常";
} else if (e instanceof IncorrectCredentialsException) {
msg = "账户/密码错误";
} else {
msg = "系统发生异常";
}
model.addAttribute("errorMsg", msg);
return "forward:/user/login";
}
/**
* 处理授权失败异常
*
* @return
*/
@ExceptionHandler(UnauthorizedException.class)
public String authorizedExceptionProcessing() {
return "error/unauthorizedException";
}
}
总结
Shiro 四个核心功能:身份认证,授权,数据加密,Seesion管理。 Shiro 三个重要角色:Subject,SecurityManager,Realm。