SpringBoot集成mybatis+PegeHelper分页插件+redis+kafka+SpringCloud(Swagger2+Feign)+shiro权限框架(分布式项目 内含源码)
SpringBoot集成mybatis+PegeHelper分页插件+redis+kafka+SpringCloud(Swagger2+Feign)+shiro权限框架(分布式项目 内含源码)
源码网盘下载地址:https://pan.baidu.com/s/1QEeIGrMC1Rc9ScCaOsbIhw
提取码:488z
虽然现在SprignBoot很火,但由于本人任职的几家公司项目都是采用常规的SSM框架搭建,再加上现在的工作偏向于前端一点,所以实际项目中一直都没有用到,正好这几天没啥事就想着利用公司的开发环境学习学习,在网上看了看资料整了一个小demo,现在整理了一下发出来,毕竟也是初学,有出入的地方还望各位大佬谅解。
一. 新建一个SpringBoot项目
1.创建一个maven工程项目
2.添加maven依赖
<!-- 定义spring-boot的版本 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath></relativePath>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- jdk版本 -->
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
<!-- spring-boot的web启动的核心jar包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<!--maven的插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- 配置java版本 不配置的话默认父类配置的是1.6 -->
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
3.创建入口类Application,加上@SpringBootApplication注解。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Jun
* @version 创建时间:2019年2月21日 上午10:40:04
* 类说明 SpringBoot启动类
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4.创建测试类TestController。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author Jun
* @version 创建时间:2019年2月22日 上午11:30:53
* 类说明
*/
@Controller
public class TestController {
@ResponseBody
@RequestMapping("/")
public String test() {
return "true";
}
}
5.在入口类Application中启动项目,启动成功如下图。
6.打开浏览器输入http://localhost:8080/,测试成功。(application配置文件不配置服务端口,SpringBoot默认为8080端口)。
7.添加SpringBoot的配置文件application.properties,配置服务访问端口号。(SpringBoot有properties和yml2种配置方式,文件名必须为application,SpringBoot加载是会默认读取application文件下的配置)
二. SpringBoot整合mybatis。
1.添加maven依赖
<!--mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 阿里系的Druid依赖包 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<!-- Druid 依赖 log4j包 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--jpa的jar包 ,操作数据库的,类似hibernate -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- spring-boot整合mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- lombok 简写代码 可用注解代替get set等 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
2.创建entity、service、dao。
(1)User实体类
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* @author Jun
* @version 创建时间:2019年2月21日 上午11:40:58
* 类说明 用户实体类
*/
@Getter
@Setter
@ToString
public class User implements Serializable{
private static final long serialVersionUID = 1L;
private String loginName;
private String password;
}
(2)Mapper接口
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.jwu.demo.entity.User;
/**
* @author Jun
* @version 创建时间:2019年2月21日 上午11:44:07
* 类说明
*/
@Mapper
public interface UserMapper {
List<User> getAll();
}
(3)Service接口
import java.util.List;
import com.jwu.demo.entity.User;
/**
* @author Jun
* @version 创建时间:2019年2月21日 上午11:42:14
* 类说明
*/
public interface IUserService {
List<User> getAll();
}
(4)Service实现类
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.jwu.demo.dao.UserMapper;
import com.jwu.demo.entity.User;
import com.jwu.demo.service.IUserService;
/**
* @author Jun
* @version 创建时间:2019年2月21日 上午11:43:04
* 类说明
*/
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
public List<User> getAll() {
return userMapper.getAll();
}
}
3.在application.properties配置文件中添加数据源以及mybatis的配置。
#数据源
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.0.37:3306/dwqmain?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
#阿里druid连接池驱动配置信息
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#连接池的配置信息
#初始化大小,最小,最大
spring.datasource.initialSize=2
spring.datasource.minIdle=2
spring.datasource.maxActive=3
#配置获取连接等待超时的时间
spring.datasource.maxWait=6000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
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
#mybatis配置
#配置Mapper文件存放的路径
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
#对应实体类所在的包
mybatis.typeAliasesPackage=com.juwu.demo.entity
4.在resources下新建mapper文件夹,然后创建UserMapper的接口映射文件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.jwu.demo.dao.UserMapper">
<select id="getAll" resultType="User">
select
login_name loginName,
user_name userName
from
bmc_user
</select>
</mapper>
5.在TestController中添加测试方法。
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.jwu.demo.entity.User;
import com.jwu.demo.service.IUserService;
/**
* @author Jun
* @version 创建时间:2019年2月22日 上午11:30:53
* @类说明 测试
*/
@Controller
public class TestController {
@Autowired
private IUserService userService;
@ResponseBody
@RequestMapping("/")
public List<User> test() {
return userService.getAll();
}
}
6.启动Application入口类,在浏览器输入http://localhost:8888/,json数据成功返回,整合mybaties成功。
三. 整合PegeHelper分页插件。
使用Mybatis时,最头痛的就是写分页,需要先写一个查询count的select语句,然后再写一个真正分页查询的语句,最后还需要自己在后台进行分页封装,PegeHelper分页插件完美的解决了这些问题。(由于PegeHelper利用了mybatis提供的拦截器,取得ThreadLocal的值,重新拼装的分页SQL完成的分页,所以在大数据量时,不利于sql的优化处理,公司有个项目当时一个分页sql 一张200多万数据的表关联2张表足足查询了20多秒,简直不能忍受,最后还是自己优化sql重新封装的分页)。
1.添加pagehelper分页的maven依赖。
<!-- pagehelper分页 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
2.在application配置文件中添加pagehelper的配置信息。
#pagehelper分页插件
pagehelper.helperDialect: mysql
pagehelper.reasonable: true
pagehelper.supportMethodsArguments: true
pagehelper.params: count=countSql
pagehelper.returnPageInfo: check
3.配置完成,在TestController中进行测试。
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jwu.demo.entity.User;
import com.jwu.demo.service.IUserService;
/**
* @author Jun
* @version 创建时间:2019年2月22日 上午11:30:53 类说明
*/
@Controller
public class TestController {
@Autowired
private IUserService userService;
@ResponseBody
@RequestMapping("/")
public PageInfo<User> test() {
PageHelper.startPage(1, 10);
List<User> userList = userService.getAll();
PageInfo<User> pageInfo = new PageInfo<>(userList);
return pageInfo;
}
}
4.浏览器访问http://localhost:8888/,返回json数据如下图。
四. 集成redis。
1.添加redismaven依赖
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring2.0集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2.在application配置文件中添加redis配置信息。
#redis
spring.redis.database=0
spring.redis.host=192.168.0.92
spring.redis.port=6377
spring.redis.password=juwudwq123!
spring.redis.timeout=5000
spring.redis.jedis.pool.max-active=10
spring.redis.jedis.pool.max-wait=5000
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.min-idle=5
3.创建Redis服务接口以及实现类。
(1)IRedisService
/**
* @author Jun
* @version 创建时间:2019年2月22日 下午5:29:59
* 类说明
*/
public interface IRedisService {
void setValue(String key,Object value);
/**
* 通过redis获取redis中的key
* @param key
* @return
*/
Object getValue(String key);
/**
* 存入redis并设置有效期
* @param key
* @param value
* @param exceed 有效期
* @param timeType 0:按天,1:按时,2:按分,3:按秒,4:按毫秒
*/
void setValue(String key,Object value,Integer exceed,Integer timeType);
}
(2)RedisServiceImpl
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.jwu.demo.service.IRedisService;
/**
* @author Jun
* @version 创建时间:2019年2月22日 下午5:30:19
* 类说明 Redis服务
*/
@Service
public class RedisServiceImpl implements IRedisService {
@Autowired
private RedisTemplate redisTemplate;
@SuppressWarnings("unchecked")
@Override
public void setValue(String key,Object value) {
redisTemplate.opsForValue().set(key, value);
}
@SuppressWarnings("unchecked")
@Override
public Object getValue(String key) {
if(!redisTemplate.hasKey(key)){
return null;
}else{
return redisTemplate.opsForValue().get(key);
}
}
@SuppressWarnings("unchecked")
@Override
public void setValue(String key, Object value, Integer exceed, Integer timeType) {
switch (timeType) {
case 0://天
redisTemplate.opsForValue().set(key, value, exceed, TimeUnit.DAYS);
break;
case 1://小时
redisTemplate.opsForValue().set(key, value, exceed, TimeUnit.HOURS);
break;
case 2: //分钟
redisTemplate.opsForValue().set(key, value, exceed, TimeUnit.MINUTES);
break;
case 3: //秒
redisTemplate.opsForValue().set(key, value, exceed, TimeUnit.SECONDS);
break;
case 4://毫秒
redisTemplate.opsForValue().set(key, value, exceed, TimeUnit.MICROSECONDS);
break;
}
}
}
4.测试代码。
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jwu.demo.entity.User;
import com.jwu.demo.service.IRedisService;
import com.jwu.demo.service.IUserService;
/**
* @author Jun
* @version 创建时间:2019年2月22日 上午11:30:53 类说明
*/
@Controller
public class TestController {
@Autowired
private IUserService userService;
@Autowired
private IRedisService redisService;
@ResponseBody
@RequestMapping("/")
public PageInfo<User> test() {
PageHelper.startPage(1, 10);
List<User> userList = userService.getAll();
PageInfo<User> pageInfo = new PageInfo<>(userList);
//存入redis并设置10秒的有效期
redisService.setValue("redisKey", pageInfo, 3, 3);
System.out.println("redisKey的值 ----------------》 " + redisService.getValue("redisKey"));
try {
Thread.sleep(3000);
System.out.println("redisKey 睡眠3秒后的值 ----------》 " + redisService.getValue("redisKey"));
} catch (InterruptedException e) {
e.printStackTrace();
}
return pageInfo;
}
}
5.测试结果。
五.集成kafka消息队列
1.添加kafka maven依赖。
<!-- kafka -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<!-- lang字符串工具 -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
2.在application配置文件中添加kafka配置信息。
#kafka
spring.applicationname=kafka-tutorial
# 指定kafka 代理地址,可以多个
spring.kafka.bootstrap-servers=192.168.0.167:9092
spring.kafka.producer.retries: 0
# 每次批量发送消息的数量
spring.kafka.producer.batch-size: 16384
# 缓存容量
spring.kafka.producer.buffer-memory: 33554432
# 指定消息key和消息体的编解码方式
spring.kafka.producer.key-serializer: org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer: org.apache.kafka.common.serialization.StringSerializer
# 指定默认消费者group id
spring.kafka.consumer.group-id=demo
spring.kafka.consumer.auto-commit-interval=100
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.enable-auto-commit=true
# 指定消息key和消息体的编解码方式
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
# 指定listener容器中的线程数,用于提高并发量
spring.kafka.listener.concurrency=3
3.新建bean包,创建kafka生产者与消费者。
(1)生产者KafkaProduction
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.stereotype.Component;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import com.alibaba.fastjson.JSON;
/**
* @author Jun
* @version 创建时间:2019年2月22日 下午3:59:44
* 类说明 Kafka生产者
*/
@Component
public class KafkaProduction<T> {
private Logger logger = LoggerFactory.getLogger(KafkaProduction.class);
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
/**
* 发送消息
* @param obj 消息体
* @param topics 消息主题
*/
public void send(T obj,String topics) {
String jsonObj = JSON.toJSONString(obj);
logger.info("----kafka---- message = {}", jsonObj);
//发送消息
ListenableFuture<SendResult<String, Object>> future = kafkaTemplate.send(topics, jsonObj);
future.addCallback(new ListenableFutureCallback<SendResult<String, Object>>() {
@Override
public void onFailure(Throwable throwable) {
logger.info("KafkaProduction: The message failed to be sent:" + throwable.getMessage());
}
@Override
public void onSuccess(SendResult<String, Object> stringObjectSendResult) { //成功消费
//TODO 业务处理
logger.info("KafkaProduction: The message was sent successfully:");
logger.info("KafkaProduction: _+_+_+_+_+_+_+ result: " + stringObjectSendResult.toString());
}
});
}
}
(2)消费者KafkaConsumer。
import java.util.Optional;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
import com.jwu.demo.constant.KafkaTopicsConstant;
/**
* @author Jun
* @version 创建时间:2019年2月22日 下午4:12:26 类说明 Kafka消费者
*/
@Component
public class KafkaConsumer {
private Logger logger = LoggerFactory.getLogger(KafkaConsumer.class);
/**
* 监听kafka.tut 的topic为test_topics的消息
* @param record
*/
@KafkaListener(topics = KafkaTopicsConstant.TEST_TOPICS)
public void listen(ConsumerRecord<?, ?> record) {
Optional<?> kafkaMessage = Optional.ofNullable(record.value());
if (kafkaMessage.isPresent()) {
Object message = kafkaMessage.get();
logger.info("KafkaConsumer Receive: +++++++++++++++ Topic:" + record.topic());
logger.info("KafkaConsumer Receive: +++++++++++++++ Record:" + record);
logger.info("KafkaConsumer Receive: +++++++++++++++ Message:" + message);
}
}
}
(3)KafkaTopicsConstant常量类
/**
* @author Jun
* @version 创建时间:2019年2月22日 下午4:37:57
* 类说明
*/
public class KafkaTopicsConstant {
/**kafka测试消息主题名*/
public final static String TEST_TOPICS = "test_topics";
}
(4)IKafkaSenderService
/**
* @author Jun
* @version 创建时间:2019年2月22日 下午4:09:36
* 类说明
*/
public interface IKafkaSenderService {
/**
* 发送主题为test_topics的消息
*/
public void send();
}
(5)KafkaSenderServiceImpl
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.jwu.demo.bean.KafkaProduction;
import com.jwu.demo.constant.KafkaTopicsConstant;
import com.jwu.demo.entity.User;
import com.jwu.demo.service.IKafkaSenderService;
/**
* @author Jun
* @version 创建时间:2019年2月22日 下午4:09:49 类说明
*/
@Service
public class KafkaSenderServiceImpl implements IKafkaSenderService {
@Autowired
private KafkaProduction<User> kafkaProduction;
@Override
public void send() {
User user = new User();
user.setLoginName("szjuwu");
user.setPassword("123456");
kafkaProduction.send(user,KafkaTopicsConstant.TEST_TOPICS);
}
}
4.测试代码。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.jwu.demo.service.IKafkaSenderService;
/**
* @author Jun
* @version 创建时间:2019年2月22日 上午11:30:53 类说明
*/
@Controller
public class TestController {
@Autowired
private IKafkaSenderService kafkaSenderService;
@ResponseBody
@RequestMapping("/")
public void test() {
kafkaSenderService.send();
}
}
5.运行结果。
六.集成SpringCloud。
- 搭建eruka服务。
(1)新建一个maven工程项目,添加eruka依赖。新建一个maven工程项目,添加eruka依赖。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<!--maven的插件 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
(2)在resources下新建application.properties配置文件。
server.port=8088
# 不向注册中心注册自己
eureka.client.register-with-eureka=false
# 不需要检索服务
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
(3)新建入口类Application并启动。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author Jun
* @version 创建时间:2019年2月21日 上午10:40:04 类说明
*/
@EnableEurekaServer
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
(4)在浏览器中输入http://localhost:8088,Eureka注册中心搭建完成。
2.搭建服务端提供者。(直接在最开始项目的基础上面做修改)。
(1)添加maven依赖。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- SpringCloud start -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- SpringCloud end -->
(2)在application配置文件中添加eureka配置信息。
#服务名称
spring.application.name=demo-server
##服务注册和发现中心
eureka.client.serviceUrl.defaultZone=http://localhost:8088/eureka
(3)在application入口类@EnableDiscoveryClient、@EnableCircuitBreaker、@EnableFeignClients添加注解并启动。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author Jun
* @version 创建时间:2019年2月21日 上午10:40:04 类说明
*/
@EnableDiscoveryClient
@EnableCircuitBreaker
@EnableFeignClients
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
(4)先启动eureka服务,再启动demo-server,输入http://localhost:8088/,下图表示demo-server服务已成功注册在eureka上。
3.服务端集成Swagger2,方便开发人员接口的对接与调式。
(1)在pom.xml中加入Swagger2的依赖
<!-- swagger start -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>
<!-- swagger end -->
(2)在Application.java同级包下创建Swagger2的配置类Swagger2。通过@Configuration注解,让Spring来加载该类配置。再通过@EnableSwagger2注解来启用Swagger2。
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author Jun
* @version 创建时间:2019年2月27日 下午5:46:47 类说明
*/
@Configuration
@EnableSwagger2
public class Swagger2 {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
.apis(RequestHandlerSelectors.basePackage("com.jwu.demo"))
.paths(PathSelectors.any()).build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("Spring Cloud demo API文档")// 标题
.version("1.0.0") // 版本
.build();
}
}
(3)创建SwaggerController类进行测试。
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.jwu.demo.entity.User;
import com.jwu.demo.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
* @author Jun
* @version 创建时间:2019年2月27日 下午6:00:59
* 类说明
*/
@Api(tags = "Swagger测试",produces = MediaType.APPLICATION_JSON_VALUE)
@RestController
@RequestMapping(value = "/swagger", produces = MediaType.APPLICATION_JSON_VALUE)
public class SwaggerController {
@Autowired
private IUserService userService;
@ApiOperation(value = "获取所有用户信息", notes = "获取所有用户信息")
@RequestMapping(value = "/getAll", method = RequestMethod.GET)
public List<User> getFansPage() {
return userService.getAll();
}
}
(4)启动erueka服务和demo-server服务,在浏览器中输入http://localhost:8888/swagger-ui.html#访问。
4.搭建客户端消费。(使用Spring Cloud Feign作为HTTP客户端调用远程HTTP服务)。
(1)新建一个SpringBoot项目,这里就直接上代码了。
maven依赖
<!-- spring-boot的父引用 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath></relativePath>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!--web支持 依赖 -->
<!-- SpringCloud start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- SpringCloud end -->
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<!--maven的插件 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
application.properties配置文件
spring.application.name=demo-web
server.port=8080
#eureka注册中心发现地址
eureka.client.service-url.defaultZone=http://localhost:8088/eureka
eureka.instance.appname=demo-web
Application启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author Jun
* @version 创建时间:2019年2月21日 上午10:40:04 类说明
*/
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
新建一个UserRemote类利用Feign调用服务端的接口
import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.juwu.demo.web.entity.User;
import feign.Headers;
/**
* @author Jun
* @version 创建时间:2019年2月27日 下午8:27:40
* 类说明
*/
@FeignClient(name = "demo-server")
@Headers("Content-Type: application/json;charset=UTF-8")
public interface UserRemote {
@RequestMapping(value="/swagger/getAll", method=RequestMethod.GET)
List<User> getAll();
}
User实体类
import java.io.Serializable;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* @author Jun
* @version 创建时间:2019年2月21日 上午11:40:58
* 类说明
*/
@Getter
@Setter
@ToString
public class User implements Serializable{
private static final long serialVersionUID = 1L;
private String loginName;
private String password;
}
LoginController
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.juwu.demo.web.entity.User;
import com.juwu.demo.web.remote.UserRemote;
/**
* @author Jun
* @version 创建时间:2019年2月27日 下午6:44:29
* 类说明
*/
@Controller
public class LoginController {
@Autowired
private UserRemote userRemote;
@RequestMapping("/getAll")
@ResponseBody
public List<User> getAll() {
List<User> userList = userRemote.getAll();
return userList;
}
}
依次启动Eureka注册中心、服务端服务、客户端项目,在浏览器输入http://localhost:8080/getAll,远程调用成功,返回JSON数据。
七.集成shiro权限框架
由于现有数据库没有角色、权限等表,加上没有整合前端页面,故这里只做登录测试,如果要进行权限认证的话,还需在页面添加对应Shiro权限标签。(在页面上加shiro标签的时候,shiro会调用doGetAuthorizationInfo方法判断当前用户有没有这个权限)。
<!-- shiro整合springboot所需相关依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2.自定义一个UserRealm类继承AuthorizingRealm,获取Shiro应用配置Realm中的用户及其权限信息。
import java.util.Set;
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.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.juwu.demo.web.entity.User;
import com.juwu.demo.web.utils.Md5Util;
/**
* @author Jun
* @version 创建时间:2019年2月22日 上午11:12:33
* 类说明
*/
@Component
public class UserRealm extends AuthorizingRealm {
public static final Logger logger = LoggerFactory.getLogger(UserRealm.class);
/**
* 用户角色和权限授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection Principal){
String loginName = (String) Principal.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
try {
//获取当前登录用户的所有角色以及权限存入shiro去做权限认证
/*
Set<String> roleName = userService.getRoleName(loginName); //获取登录用户的所有角色
Set<String> permissions = userService.getPermissions(loginName); //获取登录用户的所有权限
authorizationInfo.setRoles(roleName);
authorizationInfo.setStringPermissions(permissions);*/
} catch (Exception e) {
logger.error("UserRealm doGetAuthorizationInfo",e);
}
logger.info("authorizationInfo : {}" + authorizationInfo);
return authorizationInfo;
}
/**
* 用户身份登陆认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
String loginName = token.getUsername(); //获取登录的用户名
logger.info("doGetAuthenticationInfo loginName --------->:" + loginName);
try {
//根据登录名获取用户信息
//User user = userService.getByLoginName(loginName);
//构造user测试数据
User user = new User();
user.setLoginName("szjuwu");
user.setPassword(Md5Util.MD5("123456"));
if(user != null){
AuthenticationInfo authcInfo= new SimpleAuthenticationInfo(user.getLoginName(),user.getPassword(),getName());
return authcInfo;
}
} catch (Exception e) {
logger.error("UserRealm doGetAuthenticationInfo ============ 身份认证失败",e);
}
return null;
}
}
3.创建ShiroConfig配置类。
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.juwu.demo.web.bean.UserRealm;
import org.apache.shiro.mgt.SecurityManager;
/**
* @author Jun
* @version 创建时间:2019年2月22日 下午1:41:43
* 类说明
*/
@Configuration
public class ShiroConfig {
@Autowired
private UserRealm userRealm;
/**
* 配置shiro过滤器
* @author zhengkai
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager")SecurityManager securityManager) {
//1.定义shiroFactoryBean
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
//2.设置securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
//3.LinkedHashMap是有序的,进行顺序拦截器配置
Map<String,String> filterChainMap = new LinkedHashMap<String,String>();
//4.配置logout过滤器
filterChainMap.put("/logout", "logout");
//登陆和主页不需要认证
filterChainMap.put("/login","anon");
filterChainMap.put("/","anon");
filterChainMap.put("/checkLogin","anon");
filterChainMap.put("/css/**", "anon"); //匿名访问静态资源
filterChainMap.put("/js/**", "anon"); //匿名访问静态资源
filterChainMap.put("/fonts/**", "anon"); //匿名访问静态资源
filterChainMap.put("/images/**", "anon"); //匿名访问静态资源
filterChainMap.put("/lib/**", "anon"); //匿名访问静态资源
//5.所有url必须通过认证才可以访问
filterChainMap.put("/**","authc");
//6.设置默认登录的url
shiroFilterFactoryBean.setLoginUrl("/login");
//7.设置成功之后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/getAll");
//8.设置未授权界面
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
//9.设置shiroFilterFactoryBean的FilterChainDefinitionMap
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
return shiroFilterFactoryBean;
}
/**
* 配置安全管理器
* @author zhengkai
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置realm.
securityManager.setRealm(userRealm);//将自定义的realm注入到securityManager中
return securityManager;
}
}
4.MD5工具类。
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import org.apache.commons.codec.binary.Hex;
/**
* @author Jun
* @version 创建时间:2019年2月22日 上午11:37:06 类说明
*/
public class Md5Util {
/**
* 普通MD5
*
* @author daniel
* @time 2016-6-11 下午8:00:28
* @param inStr
* @return
*/
public static String MD5(String input) {
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
return "check jdk";
} catch (Exception e) {
e.printStackTrace();
return "";
}
char[] charArray = input.toCharArray();
byte[] byteArray = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++)
byteArray[i] = (byte) charArray[i];
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16)
hexValue.append("0");
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
/**
* 文件的MD5
* @param filePath
* @return
*/
public static String getFileMD5(String filePath){
String value = null;
FileInputStream in = null;
try {
File file = new File(filePath);
in = new FileInputStream(file);
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[8192];
int c;
while ((c = in.read(buffer)) != -1) {
md5.update(buffer, 0, c);
}
BigInteger bi = new BigInteger(1, md5.digest());
value = bi.toString(16).toUpperCase();
} catch (Exception e) {
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
}
}
}
return value;
}
/**
* 加盐MD5
*
* @author daniel
* @time 2016-6-11 下午8:45:04
* @param password
* @return
*/
public static String generate(String password) {
Random r = new Random();
StringBuilder sb = new StringBuilder(16);
sb.append(r.nextInt(99999999)).append(r.nextInt(99999999));
int len = sb.length();
if (len < 16) {
for (int i = 0; i < 16 - len; i++) {
sb.append("0");
}
}
String salt = sb.toString();
password = md5Hex(password + salt);
char[] cs = new char[48];
for (int i = 0; i < 48; i += 3) {
cs[i] = password.charAt(i / 3 * 2);
char c = salt.charAt(i / 3);
cs[i + 1] = c;
cs[i + 2] = password.charAt(i / 3 * 2 + 1);
}
return new String(cs);
}
/**
* 校验加盐后是否和原文一致
*
* @author daniel
* @time 2016-6-11 下午8:45:39
* @param password
* @param md5
* @return
*/
public static boolean verify(String password, String md5) {
char[] cs1 = new char[32];
char[] cs2 = new char[16];
for (int i = 0; i < 48; i += 3) {
cs1[i / 3 * 2] = md5.charAt(i);
cs1[i / 3 * 2 + 1] = md5.charAt(i + 2);
cs2[i / 3] = md5.charAt(i + 1);
}
String salt = new String(cs2);
return md5Hex(password + salt).equals(new String(cs1));
}
/**
* 获取十六进制字符串形式的MD5摘要
*/
private static String md5Hex(String src) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bs = md5.digest(src.getBytes());
return new String(new Hex().encode(bs));
} catch (Exception e) {
return null;
}
}
public static void main(String[] args) {
String md5 = MD5("123456");
System.out.println(md5);
}
}
5.在LoginController中添加login方法。
@RequestMapping("/login")
@ResponseBody
public String chkUser(User user,Model model){
if (user != null && StringUtils.isNotBlank(user.getLoginName())
&& StringUtils.isNotBlank(user.getPassword())) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getLoginName(), Md5Util.MD5(user.getPassword()));
try {
subject.login(token);// 会跳到我们自定义的realm中
subject.getSession().setAttribute("user", user);
logger.info(user.getLoginName() + "登录成功");
return "true";
} catch (AuthenticationException a) {
logger.warn(user.getLoginName() + "登陆系统,身份认证失败!" +
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
return "false";
}
6.依次启动Eureka注册中心、服务端服务、客户端项目,在浏览器直接输入http://localhost:8080/getAll,因为没登录被shiro拦截跳转到了刚刚配置的login路径下。
7.先在浏览器输入http://localhost:8080/login?loginName=szjuwu&password=123456进行登录。
8.登入成功后,再输入http://localhost:8080/getAll,发现这次没有被shiro拦截,成功返回数据。
OK,搞定。