spring-boot-2.0.3不一样系列之shiro - 搭建篇
Posted 青石路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring-boot-2.0.3不一样系列之shiro - 搭建篇相关的知识,希望对你有一定的参考价值。
前言
上一篇:spring-boot-2.0.3源码篇 - 国际化,讲了如何实现国际化,实际上我工作用的模版引擎是freemaker,而不是thymeleaf,不过原理都是相通的。
接着上一篇,这一篇我来讲讲spring-boot如何整合工作中用到的一个非常重要的功能:安全,而本文的主角就是一个安全框架:shiro。
Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人也越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。
摘自开涛兄的《跟我学Shiro》
本文旨在整合spring-boot与shiro,实现简单的认证功能,shiro的更多使用细节大家可以去阅读《更我学shiro》或者看官方文档。
本文项目地址:spring-boot-shiro
spring-boot整合shiro
集成mybatis
Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供,然后通过相应的接口注入给Shiro;既然用户、权限这些信息需要我们自己设计、维护,那么可想而知需要进行数据库表的设计了(具体表结构看后文),既然涉及到数据库的操作,那么我们就先整合mybatis,实现数据库的操作。
pom.xml:
<?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.lee</groupId> <artifactId>spring-boot-shiro</artifactId> <version>1.0-SNAPSHOT</version> <properties> <java.version>1.8</java.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> </parent> <dependencies> <!-- mybatis相关 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.5</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
配置文件application.yml:
spring: #连接池配置 datasource: type: com.alibaba.druid.pool.DruidDataSource druid: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/spring-boot?useSSL=false&useUnicode=true&characterEncoding=utf-8 username: root password: 123456 initial-size: 1 #连接池初始大小 max-active: 20 #连接池中最大的活跃连接数 min-idle: 1 #连接池中最小的活跃连接数 max-wait: 60000 #配置获取连接等待超时的时间 pool-prepared-statements: true #打开PSCache,并且指定每个连接上PSCache的大小 max-pool-prepared-statement-per-connection-size: 20 validation-query: SELECT 1 FROM DUAL validation-query-timeout: 30000 test-on-borrow: false #是否在获得连接后检测其可用性 test-on-return: false #是否在连接放回连接池后检测其可用性 test-while-idle: true #是否在连接空闲一段时间后检测其可用性 #mybatis配置 mybatis: type-aliases-package: com.lee.shiro.entity #config-location: classpath:mybatis/mybatis-config.xml mapper-locations: classpath:mybatis/mapper/*.xml # pagehelper配置 pagehelper: helperDialect: mysql #分页合理化,pageNum<=0则查询第一页的记录;pageNum大于总页数,则查询最后一页的记录 reasonable: true supportMethodsArguments: true params: count=countSql
在数据库spring-boot中新建表tbl_user:
DROP TABLE IF EXISTS `tbl_user`; CREATE TABLE `tbl_user` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT \'自增主键\', `username` varchar(50) NOT NULL COMMENT \'名称\', `password` char(32) NOT NULL COMMENT \'密码\', `salt` char(32) NOT NULL COMMENT \'盐,用于加密\', `state` tinyint(2) NOT NULL DEFAULT \'1\' COMMENT \'状态, 1:可用, 0:不可用\', `description` varchar(50) DEFAULT \'\' COMMENT \'描述\', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COMMENT=\'用户表\'; -- ---------------------------- -- Records of tbl_user -- ---------------------------- INSERT INTO `tbl_user` VALUES (\'1\', \'admin\', \'d3c59d25033dbf980d29554025c23a75\', \'8d78869f470951332959580424d4bf4f\', \'1\', \'bing,作者自己\'); INSERT INTO `tbl_user` VALUES (\'2\', \'brucelee\', \'5d5c735291a524c80c53ff669d2cde1b\', \'78d92ba9477b3661bc8be4bd2e8dd8c0\', \'1\', \'龙的传人\'); INSERT INTO `tbl_user` VALUES (\'3\', \'zhangsan\', \'5d5c735291a524c80c53ff669d2cde1b\', \'78d92ba9477b3661bc8be4bd2e8dd8c0\', \'1\', \'张三\'); INSERT INTO `tbl_user` VALUES (\'4\', \'lisi\', \'5d5c735291a524c80c53ff669d2cde1b\', \'78d92ba9477b3661bc8be4bd2e8dd8c0\', \'1\', \'李四\'); INSERT INTO `tbl_user` VALUES (\'5\', \'jiraya\', \'5d5c735291a524c80c53ff669d2cde1b\', \'78d92ba9477b3661bc8be4bd2e8dd8c0\', \'1\', \'自来也\');
mapper接口:UserMapper.java
package com.lee.shiro.mapper; import com.lee.shiro.entity.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; @Mapper public interface UserMapper { /** * 根据用户名获取用户 * @param username * @return */ User findUserByUsername(@Param("username") String username); }
UserMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.lee.shiro.mapper.UserMapper"> <select id="findUserByUsername" resultType="User"> SELECT id,username,password,salt,state,description FROM tbl_user WHERE username=#{username} </select> </mapper>
service接口:IUserService.java
package com.lee.shiro.service; import com.lee.shiro.entity.User; public interface IUserService { /** * 根据用户名获取用户 * @param username * @return */ User findUserByUsername(String username); }
service实现:UserServiceImpl.java
package com.lee.shiro.service.impl; import com.lee.shiro.entity.User; import com.lee.shiro.mapper.UserMapper; import com.lee.shiro.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserServiceImpl implements IUserService { @Autowired private UserMapper userMapper; @Override public User findUserByUsername(String username) { User user = userMapper.findUserByUsername(username); return user; } }
启动类:ShiroApplication.java
package com.lee.shiro; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ShiroApplication { public static void main(String[] args) { SpringApplication.run(ShiroApplication.class, args); } }
测试类:MybatisTest.java
package com.lee.shiro.test; import com.lee.shiro.ShiroApplication; import com.lee.shiro.entity.User; import com.lee.shiro.service.IUserService; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = ShiroApplication.class) public class MybatisTest { @Autowired private IUserService userService; @Test public void testFindUserByUsername() { User user = userService.findUserByUsername("brucelee"); Assert.assertEquals(user.getDescription(), "龙的传人"); } }
测试用例顺利通过,则表示mybatis集成成功
开启logback日志
其实上面的pom配置已经引入了日志依赖,如图:
但是你会发现,spring-boot-starter-logging引入了3种类型的日志,你用其中任何一种都能正常打印日志;但是我们需要用3种吗?根本用不到,我们只要用一种即可,至于选用那种,全凭大家自己的喜欢;我了,比较喜欢logback(接触的项目中用的比较多,说白了就是这3种中最熟悉的把);我们来改下pom.xml,重新配置日志依赖:
<?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.lee</groupId> <artifactId>spring-boot-shiro</artifactId> <version>1.0-SNAPSHOT</version> <properties> <java.version>1.8</java.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> </parent> <dependencies> <!-- mybatis相关 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.5</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- 日志 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> <exclusions> <!-- 剔除spring-boot-starter-logging中的全部依赖 --> <exclusion> <groupId>*</groupId> <artifactId>*</artifactId> </exclusion> </exclusions> <scope>test</scope> <!-- package或install的时候,spring-boot-starter-logging.jar也不会打进去 --> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency> <!-- test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
logback.xml:
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 --> <property name="LOG_HOME" value="/log" /> <!-- 控制台输出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>%d{yyyy-MM-dd HH:mm:ss} |%logger| |%level|%msg%n</pattern> </encoder> </appender> <!-- 按照每天生成日志文件 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日志文件输出的文件名 --> <FileNamePattern>${LOG_HOME}/spring-boot-shiro.log.%d{yyyy-MM-dd}.log</FileNamePattern> <!--日志文件保留天数 --> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 --> <spring-boot-2.0.3不一样系列之源码篇 - run方法之createApplicationContext,绝对有值得你看的地方spring-boot-2.0.3不一样系列之源码篇 - run方法之prepareContext,绝对有值得你看的地方
spring-boot-2.0.3不一样系列之源码篇 - SpringApplication的run方法之SpringApplicationRunListener,绝对有值得你看的地方
spring-boot-2.0.3之redis缓存实现,不是你想的那样哦