文章目录


闲话不多说,直接上干货
源码地址: link

一、添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.joecy</groupId>
	<artifactId>springboot_mybatis-plus_shiro</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>springboot_mybatis-plus_shiro</name>
	<description>Spring Boot整合Mybitis Plus和Shiro小Demo</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.9.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<properties>
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<mybatisplus-spring-boot-starter.version>1.0.5</mybatisplus-spring-boot-starter.version>
		<mybatisplus.version>2.1.4</mybatisplus.version>
		<shiro-spring.version>1.3.2</shiro-spring.version>
		<druid.version>1.0.18</druid.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
        <!--没有此依赖shiro注解权限会失效-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-logging</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-freemarker</artifactId>
		</dependency>
		<!--lombok插件使用依赖-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<!--mybatis plus依赖-->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatisplus-spring-boot-starter</artifactId>
			<version>${mybatisplus-spring-boot-starter.version}</version>
		</dependency>
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus</artifactId>
			<version>${mybatisplus.version}</version>
		</dependency>
		<!--数据源相关-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>${druid.version}</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<!--shiro依赖-->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>${shiro-spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<!--热部署-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>springloaded</artifactId>
		</dependency>
	</dependencies>
</project>

二、application.properties配置文件

server.port=8080
# 数据库访问配置
# 主数据源,默认的
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus_shiro_test?Unicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456

# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
#spring.datasource.useGlobalDataSourceStat=true

#集成Freemark
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=true
spring.freemarker.expose-session-attributes=true
spring.freemarker.request-context-attribute=request
spring.freemarker.allow-session-override=true
#Mybatis配置
mybatis-plus.mapper-locations=classpath:mappers/*.xml
#mybatis-plus的model包
mybatis-plus.typeAliasesPackage=com.joecy.entity
#global-config:
#主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
#id-type: 2
mybatis-plus.global-config.id-type=0
#驼峰式命名映射
mybatis-plus.global-config.db-column-underline=true

三、mybatis-plus相关

1 新建数据库表对应的model,以SecUser为例

/**
 * 用户表
 * 需要继承mybatis-plus的Model<>抽象类
 */
@Data
public class SecUser extends Model<SecUser> {

    //指定id
    @TableId
    private Integer id;
    private String username;
    private String password;
    @TableField(exist = false)
    private List<SecRole> roles;

    //实现默认id方法
    //若一个表没有id 直接return null即可
    @Override
    protected Serializable pkVal() {
        return this.id;
    }
}

2 创建SecUser对应Mapper SecUserMapper

//必须添加mybatis的@Mapper注解
@Mapper
public interface SecUserMapper extends BaseMapper<SecUser> {
}

至此该model就可以正常使用了,具体使用方法很简单,参照mybatis-plus官网:link

四、现在开始Shiro配置

1 创建MyShiroRealm

//实现AuthorizingRealm接口用户用户认证
public class MyShiroRealm extends AuthorizingRealm {

    //角色权限和对应权限添加
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //添加角色和权限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        System.out.println(principalCollection.getPrimaryPrincipal());
        SecUser user= (SecUser) principalCollection.getPrimaryPrincipal();
        //保存所有角色名
        Set<String> allRoles = new HashSet<>();
        //保存所有权限名
        Set<String> allPermissions = new HashSet<>();
        //查询对应角色
        List<SecUserRole> secUserRoles = new SecUserRole().selectList(new EntityWrapper().eq("user_id", user.getId()));
        for (SecUserRole userRole:secUserRoles) {
            SecRole role = new SecRole();
            role.setId(userRole.getRoleId());
            role = role.selectById();
            allRoles.add(role.getName());

            //查询所有权限
            List<SecPermission> permissions = new ArrayList<>();
            List<SecRolePermission> rolePermissions = new SecRolePermission().selectList(new EntityWrapper().eq("role_id", role.getId()));
            for (SecRolePermission rolePermission:rolePermissions) {
                SecPermission permission = new SecPermission();
                permission.setId(rolePermission.getPermissionId());
                permission = permission.selectById();
                allPermissions.add(permission.getName());
            }
        }
        //添加角色
        simpleAuthorizationInfo.addRoles(allRoles);
        simpleAuthorizationInfo.addStringPermissions(allPermissions);


        System.out.println(user);

        return simpleAuthorizationInfo;
    }

    //用户认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 1、登录认证的方法需要先执行,需要用他来判断登录的用户信息是否合法
        String username = (String) token.getPrincipal();    // 取得用户名
        // 需要通过用户名取得用户的完整信息,利用业务层操作
        SecUser user = null;
        try {
            user = new SecUser().selectOne(new EntityWrapper().eq("username",username));
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (user == null) {
            throw new UnknownAccountException("该用户名称不存在!");
        } else {    // 进行密码的验证处理
            String password =new String((char[]) token.getCredentials());
            // 将数据库中的密码与输入的密码进行比较,这样就可以确定当前用户是否可以正常登录
            if (user.getPassword().equals(password)) {    // 密码正确

                AuthenticationInfo auth = new SimpleAuthenticationInfo(user, password, "memberRealm");
                return auth;
            } else {
                throw new IncorrectCredentialsException("密码错误!");
            }
        }
    }
}

2 创建Shiro配置类ShiroConfig

@Configuration
public class ShiroConfig {

    //将自己的验证方式加入容器
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        return myShiroRealm;
    }

    //权限管理,配置主要是Realm的管理认证
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    //Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");


        // 权限控制map.
        LinkedHashMap<String, String> filterChainDefinitionMap=new LinkedHashMap<>();
        filterChainDefinitionMap.put("/css/**", "anon"); //表示可以匿名访问
        filterChainDefinitionMap.put("/js/**", "anon"); //表示可以匿名访问
        filterChainDefinitionMap.put("/img/**", "anon"); //表示可以匿名访问
        filterChainDefinitionMap.put("/font/**", "anon"); //表示可以匿名访问
        filterChainDefinitionMap.put("/images/**", "anon"); //表示可以匿名访问
        filterChainDefinitionMap.put("/login", "anon"); //表示可以匿名访问
        filterChainDefinitionMap.put("/user_login", "anon");
        filterChainDefinitionMap.put("/logout*","logout");
        filterChainDefinitionMap.put("/error","anon");
        filterChainDefinitionMap.put("/**","authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    //加入注解的使用,不加入这个注解不生效
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        System.out.println("开启了shiro注解功能");
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

3 创建Shiro权限不足处理类ExceptionConf

/**
 * @author 陈玉林
 * 该类配置Shiro未授权跳转
 */
@Configuration
public class ExceptionConf {
    @Bean
    public SimpleMappingExceptionResolver resolver() {
        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/403");
        resolver.setExceptionMappings(properties);
        return resolver;
    }

}

五、创建LoginController

@Controller
public class LoginController {
    @GetMapping(value = "/login")
    public String login(){
        return "login";
    }

    @PostMapping(value = "/user_login")
    public String login(@RequestParam String username, @RequestParam String password, Model model){
        try {
            model.addAttribute("username", username);
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
            Subject subject = SecurityUtils.getSubject();
            //完成登录
            subject.login(usernamePasswordToken);
            return "redirect:/index";
        } catch (Exception e) {
            String ex = e.getClass().getName();
            if (ex != null) {
                if (UnknownAccountException.class.getName().equals(ex)) {
                    System.out.println("用户名不存在");
                } else if (IncorrectCredentialsException.class.getName().equals(ex)) {
                    System.out.println("账户或密码错误");
                } else {
                    System.out.println("未知错误");
                }
            }
            //返回登录页面
            return "login";
        }
    }

    @ResponseBody
    @GetMapping(value = "/index")
    public String index(){
        return "welcome";
    }


    @RequestMapping("/logout")
    public String logOut(HttpSession session) {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login";
    }



    //未授权mapping配置
    @ResponseBody
    @GetMapping(value = "/403")
    public String error(){
        return "没有权限!";
    }


    //注解的使用
    //管理员角色
    @ResponseBody
    @RequiresRoles("admin")
    @RequestMapping(value = "/role/admin")
    public String roleAdmin(){
        return "I am admin";
    }

    //普通用户角色
    @ResponseBody
    @RequiresRoles("common")
    @RequestMapping(value = "/role/common")
    public String roleCommon(){
        return "I am common";
    }

    //拥有添加、删除权限
    @ResponseBody
    @RequiresPermissions({"add","delete"})
    @RequestMapping(value = "/permissions")
    public String Permissions(){
        return "I have permissions add and delete";
    }

    //只拥有添加权限
    @ResponseBody
    @RequiresPermissions("add")
    @RequestMapping(value = "/permission/add")
    public String Permission(){
        return "I have permission add";
    }

}

六、创建一个登录界面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
<form action="/user_login" method="post">
    <input type="text" name="username" placeholder="用户名"  >
    <input type="password" name="password" placeholder="密码"  >
    <input type="submit" value="提交">
</form>
</body>
</html>

七、创建数据库,导入doc目录下的sql文件

SpringBoot整合Shiro+mybatis-plus-LMLPHP

10-07 11:42