<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.0.RELEASEversion>
<relativePath/>
parent>
<groupId>com.jackgroupId>
<artifactId>demoartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>warpackaging>
<name>shironame>
<description>Demo project for Spring shirodescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.1.0version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.1.0version>
dependency>
<dependency>
<groupId>org.freemarkergroupId>
<artifactId>freemarkerartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
<version>1.5.3version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.5.3version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.22version>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.10version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
<version>5.2.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-jasperartifactId>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
## database ##
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db_shiro?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
## mybatis plus ##
mybatis-plus.mapper-locations=mapper/*.xml
mybatis-plus.typeAliasesPackage=com.jack.demo.entity
mybatis-plus.global-config.db-config.logic-delete-value=9
mybatis-plus.global-config.db-config.logic-not-delete-value=1
## jsp ##
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
spring.mvc.throw-exception-if-no-handler-found=true
spring.mvc.static-path-pattern=/**
package com.jack.demo;
import com.jack.demo.entity.Permission;
import com.jack.demo.entity.Role;
import com.jack.demo.entity.User;
import com.jack.demo.service.UserService;
import org.apache.commons.collections.CollectionUtils;
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.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @program: demo
* @description: 认证登录
* @author: Jack.Fang
* @date:2020-06-02 1432
**/
public class AuthRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User)principalCollection.fromRealm(this.getClass().getName()).iterator().next();
List<String> permissionList = new ArrayList<>();
List<String> roleNameList = new ArrayList<>();
Set<Role> roleSet = user.getRoles();
if(CollectionUtils.isNotEmpty(roleSet)){
for(Role role:roleSet){
roleNameList.add(role.getRname());
Set<Permission> permissionSet = role.getPermissions();
if(CollectionUtils.isNotEmpty(permissionSet)){
for(Permission permission:permissionSet){
permissionList.add(permission.getName());
}
}
}
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permissionList);
info.addRoles(roleNameList);
return info;
}
//认证登录
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
String username = usernamePasswordToken.getUsername();
User user = userService.findByUserName(username);
return new SimpleAuthenticationInfo(user==null?new User():user,user==null?null:user.getPassword(),this.getClass().getName());
}
}
package com.jack.demo;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
/**
* @program: demo
* @description: 密码校验规则重写(自定义MD5验证等)
* @author: Jack.Fang
* @date:2020-06-02 1445
**/
public class CredentialMatcher extends SimpleCredentialsMatcher {
// 密码校验规则重写
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
String password = new String(usernamePasswordToken.getPassword());
String dbPassword = (String)info.getCredentials();
return this.equals(password,dbPassword);
}
}
package com.jack.demo;
import org.apache.shiro.mgt.SecurityManager;
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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
/**
* @program: demo
* @description: Shiro 配置
* @author: Jack.Fang
* @date:2020-06-02 1451
**/
@Configuration
public class ShiroConfiguration {
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(manager);
bean.setLoginUrl("/login");
bean.setSuccessUrl("/index");
bean.setUnauthorizedUrl("/unauthorized");
//取决于DefaultFilter枚举中的值(满足条件后所调用的拦截器)
LinkedHashMap<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/index","authc");//需要验证
filterChainDefinitionMap.put("/login","anon");//无需验证
filterChainDefinitionMap.put("/loginUser", "anon");//无需验证
filterChainDefinitionMap.put("/admin", "roles[admin]");//需要管理员登录
filterChainDefinitionMap.put("/edit", "perms[edit]");//需要编辑权限
filterChainDefinitionMap.put("/druid/**", "anon");//无需验证
filterChainDefinitionMap.put("/**", "user");//用户级别(只验证是否登录)
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
@Bean("securityManager")
public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(authRealm);
return manager;
}
@Bean("authRealm")
public AuthRealm authRealm(@Qualifier("credentialMatcher") CredentialMatcher credentialMatcher){
AuthRealm authRealm = new AuthRealm();
authRealm.setCredentialsMatcher(credentialMatcher);
return authRealm;
}
@Bean("credentialMatcher")
public CredentialMatcher credentialMatcher(){
return new CredentialMatcher();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager manager){
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(manager);
return advisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
}
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 80019
Source Host : localhost:3306
Source Database : db_shiro
Target Server Type : MYSQL
Target Server Version : 80019
File Encoding : 65001
Date: 2020-06-01 17:30:22
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
`pid` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '',
`url` varchar(255) DEFAULT '',
PRIMARY KEY (`pid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES ('1', 'add', '');
INSERT INTO `permission` VALUES ('2', 'delete', '');
INSERT INTO `permission` VALUES ('3', 'edit', '');
INSERT INTO `permission` VALUES ('4', 'query', '');
-- ----------------------------
-- Table structure for permission_role
-- ----------------------------
DROP TABLE IF EXISTS `permission_role`;
CREATE TABLE `permission_role` (
`rid` int NOT NULL,
`pid` int NOT NULL,
KEY `idx_rid` (`rid`),
KEY `idx_pid` (`pid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of permission_role
-- ----------------------------
INSERT INTO `permission_role` VALUES ('1', '1');
INSERT INTO `permission_role` VALUES ('1', '2');
INSERT INTO `permission_role` VALUES ('1', '3');
INSERT INTO `permission_role` VALUES ('1', '4');
INSERT INTO `permission_role` VALUES ('2', '1');
INSERT INTO `permission_role` VALUES ('2', '4');
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`rid` int NOT NULL AUTO_INCREMENT,
`rname` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`rid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', 'admin');
INSERT INTO `role` VALUES ('2', 'customer');
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`uid` int NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL DEFAULT '',
`password` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'admin', '123');
INSERT INTO `user` VALUES ('2', 'demo', '123');
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`uid` int NOT NULL,
`rid` int NOT NULL,
KEY `idx_uid` (`uid`),
KEY `idx_rid` (`rid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('1', '1');
INSERT INTO `user_role` VALUES ('2', '2');
package com.jack.demo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
*
*
*
*
* @author jack
* @since 2020-06-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class Permission extends Model<Permission> {
private static final long serialVersionUID = 1L;
@TableId(value = "pid", type = IdType.AUTO)
private Integer pid;
private String name;
private String url;
@Override
protected Serializable pkVal() {
return this.pid;
}
}
package com.jack.demo.entity;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
*
*
*
*
* @author jack
* @since 2020-06-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class PermissionRole extends Model<PermissionRole> {
private static final long serialVersionUID = 1L;
private Integer rid;
private Integer pid;
@Override
protected Serializable pkVal() {
return null;
}
}
package com.jack.demo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
*
*
*
*
* @author jack
* @since 2020-06-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class Role extends Model<Role> {
private static final long serialVersionUID = 1L;
@TableId(value = "rid", type = IdType.AUTO)
private Integer rid;
private String rname;
private Set<Permission> permissions = new HashSet<>();
private Set<User> users = new HashSet<>();
@Override
protected Serializable pkVal() {
return this.rid;
}
}
package com.jack.demo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
*
*
*
*
* @author jack
* @since 2020-06-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class User extends Model<User> {
private static final long serialVersionUID = 1L;
@TableId(value = "uid", type = IdType.AUTO)
private Integer uid;
private String username;
private String password;
private Set<Role> roles = new HashSet<>();
@Override
protected Serializable pkVal() {
return this.uid;
}
}
package com.jack.demo.entity;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
*
*
*
*
* @author jack
* @since 2020-06-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class UserRole extends Model<UserRole> {
private static final long serialVersionUID = 1L;
private Integer uid;
private Integer rid;
@Override
protected Serializable pkVal() {
return null;
}
}
package com.jack.demo.service;
import com.jack.demo.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;
/**
*
* 服务类
*
*
* @author jack
* @since 2020-06-01
*/
public interface UserService extends IService<User> {
User findByUserName(String username);
}
package com.jack.demo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jack.demo.entity.User;
import com.jack.demo.mapper.UserMapper;
import com.jack.demo.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
*
* 服务实现类
*
*
* @author jack
* @since 2020-06-01
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public User findByUserName(String username) {
/*QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("username",username);
return baseMapper.selectOne(queryWrapper);*/
return baseMapper.findByUserName(username);
}
}
package com.jack.demo.mapper;
import com.jack.demo.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
*
* Mapper 接口
*
*
* @author jack
* @since 2020-06-01
*/
public interface UserMapper extends BaseMapper<User> {
User findByUserName(String username);
}
<mapper namespace="com.jack.demo.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.jack.demo.entity.User">
<id column="uid" property="uid" />
<result column="username" property="username" />
<result column="password" property="password" />
resultMap>
<resultMap id="userMap" type="com.jack.demo.entity.User">
<id property="uid" column="uid" />
<result property="username" column="username" />
<result property="password" column="password" />
<collection property="roles" ofType="com.jack.demo.entity.Role">
<id property="rid" column="rid" />
<result property="rname" column="rname" />
<collection property="permissions" ofType="com.jack.demo.entity.Permission">
<id property="pid" column="pid" />
<result property="name" column="name"/>
<result property="url" column="url" />
collection>
collection>
resultMap>
<select id="findByUserName" parameterType="string" resultMap="userMap">
select u.*,r.*,p.*
from `user` u
inner join user_role ur on u.uid=ur.uid
inner join role r on ur.rid=r.rid
inner join permission_role pr on r.rid=pr.rid
inner join permission p on pr.pid=p.pid
where u.username=#{username}
select>
mapper>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hometitle>
head>
<body>
<h1> 欢迎登录, ${user.username} h1>
body>
html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hometitle>
head>
<body>
<h1> 欢迎登录, ${user.username} h1>
body>
html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Unauthorizedtitle>
head>
<body>
Unauthorized!
body>
html>
package com.jack.demo.controller;
import com.jack.demo.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
/**
*
* 前端控制器
*
*
* @author jack
* @since 2020-06-01
*/
@Controller
public class UserController {
/**
* 跳转登录页
* @return
*/
@RequestMapping("/login")
public String login(){
return "login";
}
/**
* 跳转首页(需要登录验证,未登录跳转登录页)
* @return
*/
@RequestMapping("/index")
public String index(){
return "index";
}
/**
* 登出接口(成功后跳转登录页面)
* @return
*/
@RequestMapping("/logout")
public String logout() {
Subject subject = SecurityUtils.getSubject();
if (subject != null) {
subject.logout();
}
return "login";
}
/**
* 定义未授权页面
* @return
*/
@RequestMapping("unauthorized")
public String unauthorized() {
return "unauthorized";
}
/**
* 只允许admin访问的接口(非admin跳转未授权接口)
* @return
*/
@RequestMapping("/admin")
@ResponseBody
public String admin() {
return "admin success";
}
/**
* 拥有edit权限的用户才能访问
* @return
*/
@RequestMapping("/edit")
@ResponseBody
public String edit() {
return "edit success";
}
@RequestMapping("/loginUser")
public String loginUser(@RequestParam("username")String username,
@RequestParam("password")String password,
HttpSession session){
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
User user = (User)subject.getPrincipal();
session.setAttribute("user",user);
return "/index";
} catch (AuthenticationException e) {
return "/login";
}
}
}
输入 http://localhost:8080/druid/index.html 跳转登录页 http://localhost:8080/druid/login.html
登录DruidConfiguration中配置的账号密码:druid 123456
以上是springboot2.3 版本加mybatis-plus 实现与apache shiro 集成的项目,
完成了基本的权限登录与管理功能。
易用、灵活、Web能力强,容易集成其他框架spring。
学习资料少、除了自己实现RBAC外,操作界面也需要自己实现。
总体来说shiro是一款值得使用的框架,相比spring security更好用一点!