一、SpringBoot多环境配置
1.环境的配置信息
(1)application.properties
#指定默认使用dev的配置
spring.profiles.active=dev
(2)application-dev.properties
#开发环境
server.port=8080
branch=dev
(3)application-prod.properties
#测试环境
server.port=8081
branch=test
2.测试类
package com.yh.bootev.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author youHui * 检测当前使用的是哪个环境 **/ @RestController @RequestMapping("/test") public class TestController { @Value("${branch}") private String branch; @GetMapping("/branch") public String test(){ return branch; } }
3.打包后在不同环境下运行
java -jar 你打包的jar包名.jar --spring.profiles.active=你要切换的环境
如:java -jar boot-ev-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod
二、SpringBoot devtools热部署配置(自动的不建议,对性能消耗大,建议手动的)
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
2.配置plugin :加入 pligin 且配置一个属性 fork 为 true
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
3.开启自动编译
5.重启IDEA即可实现热部署。
三、SpringBoot过滤器
(一)快速了解过滤器
过滤器是基于Servlet实现的,在web开发中可以帮助过滤一些指定的url,可以过滤掉不需要的东西,比如:一些错误的请求。过滤未登录的用户等。
废话不多说直接上代码:
方式一:@WebFilter
1.创建过滤器
package com.yh.bootfilter.filter;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Author youHui
* 自定义Filter的方式有两种,一种是@WebFilter,另一种是FilterRegistrationBean
*方式一:
* @WebFilter用于将一个类声明为过滤器,该注解将在部署时被容器处理,
* 容器会根据具体的属性配置将相应的类部署为过滤器
* 其中的来个参数
* urlPatterns = "/api/*":定义要拦截的url路径
* filterName = "myFilter":指定过滤器的名称
**/
@WebFilter(urlPatterns = "/api/*",filterName = "myFilter")
@Order(1) //这个值用来控制过滤的执行顺序,若同时有多个过滤器,该值越大,越往后执行
public class MyFilter implements Filter {//1.实现Filter接口
//2.重写Filter的三个方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter被初始化了");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//获取所拦截的接口信息
HttpServletRequest request = (HttpServletRequest) servletRequest;
String uri = request.getRequestURI();
String method = request.getMethod();
System.out.println("请求的接口--"+uri+"请求的方法--"+method);
filterChain.doFilter(servletRequest,servletResponse);//只用执行了这个方法,才会丸往下执行
}
@Override
public void destroy() {
System.out.println("MyFilter被销毁");
}
}
2.测试类
package com.yh.bootfilter.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author youHui
**/
@RestController
@RequestMapping("/api")
public class FilterTestController {
@GetMapping("/user/filter")
public String hello(){
return "我被myFilter过滤器监控了";
}
}
3.注:需要在启动类中加
@ServletComponentScan注解过滤器才会生效
方式二:
通过FilterRegistrationBean注入容器的方式实现
1.创建过滤器类
package com.yh.bootfilter.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Author youHui
*FilterRegistrationBean
**/
public class MyFilter2 implements Filter {//1.实现Filter接口
//2.重写Filter的三个方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter被初始化了");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//获取所拦截的接口信息
HttpServletRequest request = (HttpServletRequest) servletRequest;
String uri = request.getRequestURI();
String method = request.getMethod();
System.out.println("请求的接口--"+uri+"请求的方法--"+method);
filterChain.doFilter(servletRequest,servletResponse);//只用执行了这个方法,才会丸往下执行
}
@Override
public void destroy() {
System.out.println("MyFilter被销毁");
}
}
2.创建配置类:目的是为了将过滤器信息注入到Spring容器中去
package com.yh.bootfilter.config;
import com.yh.bootfilter.filter.MyFilter2;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author youHui
**/
@Configuration
public class FilterConfig {
//将过滤去注入到容器中
@Bean
public MyFilter2 myFilter2(){
return new MyFilter2();
}
@Bean
public FilterRegistrationBean getFilterRegistrationBean(MyFilter2 myFilter2){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(myFilter2);//指定过滤器类
filterRegistrationBean.setOrder(1);//过滤器的顺序
filterRegistrationBean.addUrlPatterns("/api/*");//指定要拦截的接口
filterRegistrationBean.setName("myFilter");//指定过滤器名
return filterRegistrationBean;
}
}
3.测试类
package com.yh.bootfilter.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author youHui
**/
@RestController
@RequestMapping("/api")
public class FilterTestController {
@GetMapping("/user/filter")
public String hello(){
return "我被myFilter过滤器监控了";
}
}
(二)过滤器在开发中的应用
1.配置开发性接口
在application.properties文件中:
#设置白名单接口:凡是请求地址层级带有open都放行
open.url=/**/open/**
2.过滤器
package com.yh.bootfilter.filter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Author youHui
*FilterRegistrationBean
**/
public class MyFilter2 implements Filter {//1.实现Filter接口
@Value("${open.url}")
private String openUrl;//获取开发性api
//2.重写Filter的三个方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter被初始化了");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//获取所拦截的接口信息
HttpServletRequest request = (HttpServletRequest) servletRequest;
String uri = request.getRequestURI();
String method = request.getMethod();
System.out.println("请求的接口--"+uri+"请求的方法--"+method);
//判断是是开放性api
AntPathMatcher antPathMatcher = new AntPathMatcher();
if(antPathMatcher.match(openUrl,uri)){
filterChain.doFilter(servletRequest,servletResponse);//放行
}else {
//如果不是开放的接口,就判断它是否有token凭证
String token = request.getHeader("token");//获取token
if(StringUtils.isEmpty(token)){
//跳转到未登录的接口
servletRequest.getRequestDispatcher("/api/open/unLogin").forward(servletRequest, servletResponse);
}else {
filterChain.doFilter(servletRequest,servletResponse);
}
}
}
@Override
public void destroy() {
System.out.println("MyFilter被销毁");
}
}
3.
package com.yh.bootfilter.config;
import com.yh.bootfilter.filter.MyFilter2;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author youHui
**/
@Configuration
public class FilterConfig {
//将过滤去注入到容器中
@Bean
public MyFilter2 myFilter2(){
return new MyFilter2();
}
@Bean
public FilterRegistrationBean getFilterRegistrationBean(MyFilter2 myFilter2){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(myFilter2);//指定过滤器类
filterRegistrationBean.setOrder(1);//过滤器的顺序
filterRegistrationBean.addUrlPatterns("/api/*");//指定要拦截的接口
filterRegistrationBean.setName("myFilter");//指定过滤器名
return filterRegistrationBean;
}
}
4.测试类
package com.yh.bootfilter.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* @Author youHui
**/
@RestController
@RequestMapping("/api")
public class FilterTestController {
@GetMapping("/user/filter")
public String hello(){
return "我被myFilter过滤器监控了";
}
//开放性接口
@GetMapping("/home/open/info")
public String setHome(){
return "欢迎进入首页";
}
@GetMapping("/open/unLogin")
public String setUnLogin(){
return "登录失效,请重新登录";
}
}
四、SpringBoot拦截器Interceptor
简单来说,拦截器就是一道阀门,在某个方法被访问之前进行拦截,然后在之前或者之后加入某些操作,拦截器是AOP的一种实现策略。
(一)Interceptor基础
1.自定义拦截器类
package com.yh.bootinterceptor.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @Author youHui
**/
public class MyInterceptor implements HandlerInterceptor {//1.实现HandlerInterceptor接口
//2.重写它的三个方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截方法访问之前调用");
String requestUri = request.getRequestURI();//获取被拦截的接口
System.out.println(requestUri+"接口被拦截了");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截方法访问之后调用");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("整个流程结束调用");
}
}
2.定义开放的接口
#开发性接口
open.url=/**/open/**
3.创建配置类,用来配置拦截器的cel
package com.yh.bootinterceptor.config;
import com.yh.bootinterceptor.interceptor.MyInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Author youHui
**/
@Configuration
public class WebApplicationConfig implements WebMvcConfigurer {//1.实现WebMvcConfigurer
//注入开放性api
@Value("${open.url}")
private String openUrl;
//声明一个拦截器并注入到容器中
@Bean
public MyInterceptor myInterceptor(){
return new MyInterceptor();
}
//2.重写addInterceptors方法
@Override
public void addInterceptors(InterceptorRegistry registry){
/**
* 配置拦截策略
* addInterceptor(myInterceptor()):指定用哪一个拦截器
* addPathPatterns("/api/**"):所要拦截的接口
* excludePathPatterns(openUrl):开放接口
*/
registry.addInterceptor(myInterceptor()).addPathPatterns("/api/**").excludePathPatterns(openUrl);
}
}
4.测试类
package com.yh.bootinterceptor.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author youHui
**/
@RestController
@RequestMapping("/api")
public class InterceptorController {
@GetMapping("/home/open/info")
public String home(){
return "欢迎访问首页";
}
@GetMapping("/user/interceptor")
public String userInfo(){
return "已经被拦截器监控";
}
}
(二)拦截器在用户登录中的应用
1.配置类
package com.yh.bootinterceptor.config;
import com.yh.bootinterceptor.interceptor.MyInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Author youHui
**/
@Configuration
public class WebApplicationConfig implements WebMvcConfigurer {//1.实现WebMvcConfigurer
//注入开放性api
@Value("${open.url}")
private String openUrl;
//声明一个拦截器并注入到容器中
@Bean
public MyInterceptor myInterceptor(){
return new MyInterceptor();
}
//2.重写addInterceptors方法
@Override
public void addInterceptors(InterceptorRegistry registry){
/**
* 配置拦截策略
* addInterceptor(myInterceptor()):指定用哪一个拦截器
* addPathPatterns("/api/**"):所要拦截的接口
* excludePathPatterns(openUrl):开放接口
*/
registry.addInterceptor(myInterceptor()).addPathPatterns("/api/**").excludePathPatterns(openUrl);
}
}
2.拦截器类
package com.yh.bootinterceptor.interceptor;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Author youHui
**/
public class MyInterceptor implements HandlerInterceptor {//1.实现HandlerInterceptor接口
//2.重写它的三个方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截方法访问之前调用");
String requestUri = request.getRequestURI();//获取被拦截的接口
System.out.println(requestUri+"接口被拦截了");
//判断用户是否携带凭证
String token = request.getHeader("token");
if(StringUtils.isEmpty(token)){
request.getRequestDispatcher("/api/open/unLogin").forward(request,response);
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截方法访问之后调用");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("整个流程结束调用");
}
}
3.测试类
package com.yh.bootinterceptor.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author youHui
**/
@RestController
@RequestMapping("/api")
public class InterceptorController {
@GetMapping("/home/open/info")
public String home(){
return "欢迎访问首页";
}
@GetMapping("/user/interceptor")
public String userInfo(){
return "已经被拦截器监控";
}
@GetMapping("/open/unLogin")
public String getUnLogin(){
return "登录失效,请重新登录";
}
}
五、SpringBoot整合JSP
1.添加jsp相关依赖
<!--jsp标签库支持-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!--springBoot内置的tomcat对jsp的支持-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
2.jsp相关配置
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
server.port=8088
3.配置webapp的路径(创建在main目录下)
4.在jsp文件夹下新建index.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1 style="color:blue;font-weight: bold;">成功整合jsp</h1>
</body>
</html>
5.测试类
package com.yh.bootjsp.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Author youHui
**/
@Controller
@RequestMapping
public class JspController {
@GetMapping("/index")
public String index(){
return "index";//返回jsp页面
}
}
六、SpringBoot整合Thymeleaf
1.Thymeleaf配置
spring.thymeleaf.prefix=classpath:/templates
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html
2.Thymeleaf依赖
在项目创建时勾选Thymeleaf或到网上找相关依赖引入即可。
3.在templates下创建模板index.html文件
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p th:text="'hello:'+${username}"></p>
</body>
</html>
4.测试类
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model){
model.addAttribute("username","zhangsan");
return "hello";
}
}
七、SpringBoot整合Freemarker
我这里有提到(123条消息) You辉编程_Java框架之SpringBoot_You辉编程的博客-CSDN博客
八、SpringBoot集成Druid监控数据源
其实就是一个平台,通过配置进入平台查看一些数据。
推展认识:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
(2)相关配置
#数据库配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://localhost:3306/test_mybatis?
useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.druid.username=root
spring.datasource.druid.password=root
################## 连接池配置 ################
#连接池建立时创建的初始化连接数
spring.datasource.druid.initial-size=5
#连接池中最大的活跃连接数
spring.datasource.druid.max-active=20
#连接池中最小的活跃连接数
spring.datasource.druid.min-idle=5
# 配置获取连接等待超时的时间
spring.datasource.druid.max-wait=60000
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.druid.pool-prepared-statements=true
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20 spring.datasource.druid.validation-query=SELECT 1 FROM DUAL
spring.datasource.druid.validation-query-timeout=30000
#是否在获得连接后检测其可用性
spring.datasource.druid.test-on-borrow=false
#是否在连接放回连接池后检测其可用性
spring.datasource.druid.test-on-return=false
#是否在连接空闲一段时间后检测其可用性
spring.datasource.druid.test-while-idle=true
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.druid.time-between-eviction-runs-millis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.druid.min-evictable-idle-time-millis=300000
# 监控后台账号和密码
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=66666
spring.devtools.restart.poll-interval=3000ms
spring.devtools.restart.quiet-period=2999ms
(3)访问Druid:http://localhost:8080/druid
九、SpringBoot集成Swagger2
(一)使用
1.引入依赖
<!--swagger2 依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
2.创建配置
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createDocket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//要暴露的接口
.apis(RequestHandlerSelectors.basePackage("com.xxx.xxx.controller"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(parameterList)
}
//信息的描述
private ApiInfo apiInfo(){
return new ApiInfoBuilder().
title("Swagger api") //标题
.description("spring boot 2.x 集成Swagger") //描述
.version("1.0") //版本
.build();
}
}
3.描述
@Data
public class SwaggerReqVO {
@ApiModelProperty(value = "账号") //描述
private String username;
@ApiModelProperty(value = "密码")
private String password;
}
4.测试类
@RestController
@RequestMapping("/api")
@Api(tags = "测试swagger接口") //描述
public class SwaggerController {
@ApiOperation(value = "我的第一个swagger接口")
@PostMapping("/swagger")
public SwaggerReqVO testSwagger(@RequestBody SwaggerReqVO vo){
return vo;
}
}
5.访问http://localhost:8080/swagger-ui.html#/
(二)Swagger常用注解
1.@Api:对一个类进行说明。可以标记一个 Controller 类作为 Swagger 文档资源。
@Api(tags = "用户模块",description = "用户模块相关接口")
2.@ApiModel:对一个类进行说明。一般用于接受前端参数的实体类上。
@ApiModel(value = "实体类的相对路径",description = "接收更新用户数据VO")
public class UpdateUserReqVO {
@ApiModelProperty(value = "用户id")//对每个属性进行描述
private String id;
@ApiModelProperty(value = "账号")
private String username;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "性别(1.男 2.女)")
private Integer sex;
}
3.@ApiParam:主要用于Controller中方法参数的说明
value:参数说明
required:是否是必须的
@GetMapping("/getUser")
public SysUser getUser(@ApiParam(value = "需要传用户id",required = true) @RequestParam(required = true) String id){
return userService.getUserInfo(id);
}
4.@ApiOperation:对controller方法的作用进行说明。
@ApiOperation(value = "获取一个用户")
5.@ApiResponse和ApiResponses
@ApiResponse用来说明响应的是哪些信息,ApiResponses表示组装了多个@ApiResponse
@ApiResponses({
@ApiResponse(code = 0,message = "响应成功",response = SysUser.class)
})
6.@ApiImplicitParam和@ApiImplicitParams
用于方法,为单独的请求参数进行说明。
@ApiImplicitParams({
@ApiImplicitParam(name="id",value = "用户id",dataType = "String",paramType = "query",required = true,
defaultValue = "1001")
})
(三)Swagger安全配置
有的环境并不需要Swagger,这时我们可以将其关掉。
1.配置
#swagger 开关
swagger2.enable=true
2.将其注入配置类
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Value("${swagger2.enable}")
private boolean enable;
@Bean
public Docket createDocket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//要暴露的接口
.apis(RequestHandlerSelectors.basePackage("com.xxx.xxx.controller"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(parameterList)
.enable(enable);
}
//信息的描述
private ApiInfo apiInfo(){
return new ApiInfoBuilder().
title("Swagger api") //标题
.description("spring boot 2.x 集成Swagger") //描述
.version("1.0") //版本
.build();
}
}
(四)Swagger的全局配置
如果想Swagger也能像PostMan一样可以加一些头部信息的话,就需要对Swagger进行全局配置
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Value("${swagger2.enable}")
private boolean enable;
@Bean
public Docket createDocket(){
List<Parameter> parameterList=new ArrayList<>();
ParameterBuilder parameterBuilder=new ParameterBuilder();//头部信息
parameterBuilder.name("token").description("swagger调试(模拟传入用户认证凭证)").modelRef(new ModelRef("String"))
.parameterType("header").required(false);
parameterList.add(parameterBuilder.build());
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//要暴露的接口
.apis(RequestHandlerSelectors.basePackage("com.yingxue.lesson.web.controller"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(parameterList)
.enable(enable);
}
//信息的描述
private ApiInfo apiInfo(){
return new ApiInfoBuilder().
title("Swagger api") //标题
.description("spring boot 2.x 集成Swagger") //描述
.version("1.0") //版本
.build();
}
}
十、SpringBoot封装整合Redis
1.初始Redis:You辉编程_Redis数据库_You辉编程的博客-CSDN博客
2.Srpingboot+jedis
jedis集成了redis的命令擦操作,jedis是redis官方推荐面向Java操作Redis的客户端 。
(1)引入相关依赖
<!--springboot封装redis的组件依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--jedis客户端-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
(2)配置jedisPool连接池管理jedis
#jedisPool的配置开始
# 连接池中的最大空闲连接
redis.maxIdle=30
# 连接池中的最小空闲连接
redis.minIdle=1
#连接池最大连接数(使用负值表示没有限制)
redis.maxTotal=100
# 连接池最大阻塞等待时间(使用负值表示没有限制)10秒redis.maxWait=10000
# Redis服务器地址
redis.host=localhost
# Redis服务器连接端口
redis.port=6379
# Redis链接超时时间 10秒
redis.timeout=10000
(3)配置类
package com.yh.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @Author youHui
**/
@Configuration
public class RedisConfig {
@Value("${redis.maxIdle}")
private int maxIdle;
@Value("${redis.minIdle}")
private int minIdle;
@Value("${redis.maxTotal}")
private int maxTotal;
@Value("${redis.timeout}")
private int timeout;
@Value("${redis.maxWait}")
private int maxWait;
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private int port;
/**
* 把 jedisPool连接池注入到spring容器中
* @return
*/
@Bean
public JedisPool getJedisPool(){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxTotal(maxTotal);
jedisPoolConfig.setMaxWaitMillis(maxWait);
JedisPool jedisPool = new JedisPool(jedisPoolConfig,host,port,timeout);
return jedisPool;
}
}
(4)创建RedisService类,jedis企业开发工具类的封装
package com.yh.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
* @Author youHui
**/
@Service
public class RedisService {
//注入连接池
@Autowired
private JedisPool jedisPool;
public boolean exists(String key){
Jedis jedis = null;
boolean result;
try {
jedis = jedisPool.getResource();
result = jedis.exists(key);
} finally {
if(jedis != null){
jedis.close();
}
}
return result;
}
public Long del(final String... keys){
Jedis jedis = null;
Long result;
try {
jedis = jedisPool.getResource();
result = jedis.del(keys);
} finally {
if(jedis != null){
jedis.close();
}
}
return result;
}
}
(5)测试
@Autowired
private RedisService redisService;
@Test
public void testRedis(){
redisService.exists("name");
redisService.del("name","name1");
}
3.SpringBoot+RedisRedisTemplate工具类封装自定义序列化方式
RedisTemplate类是对Redis api进一步进行封装,使操作Redis更加便捷。
(1)引入相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
(2)配置
# Redis 服务器地址
spring.redis.host=localhost
# Redis 服务器连接端⼝
spring.redis.port=6379
# 连接池最大连接数(使用负值表示没有限制)默认 8
spring.redis.lettuce.pool.max-active=100
# 连接池最大阻塞等待时间(使用负值表示没有限制)默认 -1 spring.redis.lettuce.pool.max-wait=PT10S
# 连接池中的最大空闲连接默认 8
spring.redis.lettuce.pool.max-idle=30
# 连接池中的最小空闲连接默认 0
spring.redis.lettuce.pool.min-idle=1
#链接超时时间
spring.redis.timeout=PT10S
(3)测试
SpirngBoot已经自动把RedisTemplate注入到Spring容器中,直接拿来用即可。并提供了一下方法
@Test
public void testRedis(){
redisTemplate.opsForValue().get("name");
//指定序列化,使它的key,value可读即显示正常的字符
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.opsForValue().set("name","赵六");
}
注:RedisTemplate默认只支持String类型的数据,要想支持除String以外的数据,则要自己定义序列化方式(需要引入fast json依赖)。
<!--fastJson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.49</version>
</dependency>
自定义序列化方式代码
public class MyStringRedisSerializer implements RedisSerializer<Object> {
private final Charset charset;
public MyStringRedisSerializer() {
this(StandardCharsets.UTF_8);
}
public MyStringRedisSerializer(Charset charset) {
Assert.notNull(charset, "Charset must not be null!");
this.charset = charset;
}
@Override
public String deserialize(byte[] bytes) {
return (bytes == null ? null : new String(bytes, charset));
}
@Override
public byte[] serialize(Object object) {
if (object == null) {
return new byte[0];
}
if(object instanceof String){
return object.toString().getBytes(charset);
}else {
String string = JSON.toJSONString(object);
return string.getBytes(charset);
}
}
}
封装成工具类
package com.yh.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
/**
* @Author youHui
**/
@Service
public class RedisService {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
public boolean hasKey(String key){
if(key == null){
return false;
}
return redisTemplate.hasKey(key);
}
}
配置类
package com.yh.config;
import com.yh.serializer.MyStringRedisSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @Author youHui
**/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new MyStringRedisSerializer());
redisTemplate.setHashValueSerializer(new MyStringRedisSerializer());
return redisTemplate;
}
}
异常类
package com.yh.exception;
/**
* @Author youHui
**/
public class BusinessExcception extends RuntimeException{
private final int messageCode;
private final String messageDefault;
public BusinessExcception(String message,int messageCode) {
super(message);
this.messageCode = messageCode;
this.messageDefault = message;
}
public int getMessageCode() {
return messageCode;
}
public String getMessageDefault() {
return messageDefault;
}
}
测试
@Autowired
private RedisService redisService;
@Test
public void testRedis(){
// redisTemplate.opsForValue().get("name");
// //指定序列化,使它的key,value可读即显示正常的字符
// redisTemplate.setKeySerializer(new StringRedisSerializer());
// redisTemplate.setValueSerializer(new StringRedisSerializer());
// redisTemplate.opsForValue().set("name","赵六");
System.out.println(redisService.hasKey("name"));
}
4.SpringBoot redis实战-分布式Session共享
实现思路
用户提交用户名+密码--->登录成功后生成token--->把用户信息存入Redis(key就是token,value就是userId)--->设置key的过期时间(模拟Session的过期时间,一般我1h)--->拦截器拦截请求校验sessionId
boot-redis-session: redis实行分布式session共享 (gitee.com)
十一、SpringBoot集成定时任务、异步调用
1.定时任务实现方式
注:需要在启动类中加
@EnableScheduling //开启定时任务
package com.yh.bootschedule.task;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author youHui
**/
@Component
public class SchedulerTask {
private static final SimpleDateFormat f=new SimpleDateFormat("HH:mm:ss");
/**
* @Scheduled(fixedRate = 5000) :上一次开始执行时间点之后5秒再执行
* @Scheduled(fixedDelay = 5000) :上一次执行完毕时间点之后5秒再执行
* @Scheduled(initialDelay=1000, fixedRate=5000) :第一次延迟1秒后执行,之后按fixedRate的规则每5秒执行一次
*/
//方式一
//定时任务5秒执行一次
// @Scheduled(fixedRate = 5000)
// public void processFixedRate(){
// System.out.println("processFixedRate方式:开始定时任务,现在的时间是:"+f.format(new Date()));
// }
/**
* cron 一共有七位,最后一位是年,Spring Boot 定时方案中只需要设置六位即可:
* 第一位,表示秒,取值 0 ~ 59; 第二位,表示分,取值 0 ~ 59; 第三位,表示小时,取值 0 ~ 23; 第四位,日期天/日,取值 1 ~
* 31; 第五位,日期月份,取值 1~12; 第六位,星期,取值 1 ~ 7,星期一,星期二...,注,1 表示星期 天,2 表示星期一; 第七位,
* 年份,可以留空,取值 1970 ~ 2099。 cron 中,还有一些特殊的符号,含义如下: (*)星号,可以理解为每的意思,每秒、每分、
* 每天、每月、每年...。 (?)问号,问号只能出现在日期和星期这两个位置,表示这个位置的值不确定。 (-)减号,表达一个范围,
* 如在小时字段中使用“10 ~ 12”,则表示从 10 到 12 点,即 10、11、12。 (,)逗号,表达一个列表值,如在星期字段中使用“1、2、
* 4”,则表示星期一、星期二、星期四。 (/)斜杠,如 x/y,x 是开始值,y 是步⻓长,比如在第一位(秒),0/15 就是从 0 秒开始,
* 每隔 15 秒执 行一次。 下面列举几个常用的例子。 0 0 1 * * ? :每天凌晨1 点执行; 0 5 1 * * ?:每天 凌晨1 点 5 分执行;
* 以上就是 Spring Boot 自定的定时方案,使用起来非常的简单方便。
*/
//方式二
@Scheduled(cron="*/5 * * * * ?")
private void processCron(){
System.out.println("processCron方式:定时任务开始运行,现在时间:" + f.format(new Date()));
}
}
2.SpringBoot使用@Async实现异步调用-异步回调结果
注:需要在启动类中添加@EnableAsync注解
package com.yh.task;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import java.util.Random;
import java.util.concurrent.Future;
/**
* @Author youHui
**/
@Component
public class AsyncTask {
/**
* 这是同步
*/
public static Random random=new Random();
@Async
public Future<String> doTaskOne() throws Exception{
System.out.println("开始任务一");
long startTime=System.currentTimeMillis();//当前时间
Thread.sleep(random.nextInt(100000));
long endTime=System.currentTimeMillis();
System.out.println("完成任务一消耗的时间:"+(endTime-startTime)+"毫秒");
return new AsyncResult<>("任务一已完成");
}
@Async
public Future<String> doTaskTwo() throws Exception{
System.out.println("开始任务二");
long startTime=System.currentTimeMillis();//当前时间
Thread.sleep(random.nextInt(100000));
long endTime=System.currentTimeMillis();
System.out.println("完成任务二消耗的时间:"+(endTime-startTime)+"毫秒");
return new AsyncResult<>("任务一已完成");
}
@Async
public Future<String> doTaskThree() throws Exception{
System.out.println("开始任务三");
long startTime=System.currentTimeMillis();//当前时间
Thread.sleep(random.nextInt(100000));
long endTime=System.currentTimeMillis();
System.out.println("完成任务三消耗的时间:"+(endTime-startTime)+"毫秒");
return new AsyncResult<>("任务一已完成");
}
}
测试:
@Test
public void testTask() throws Exception{
Future<String> taskOne=asyncTask.doTaskOne();
Future<String> taskTwo=asyncTask.doTaskTwo();
Future<String> taskThree=asyncTask.doTaskThree();
while (true){
if(taskOne.isDone()&&taskTwo.isDone()&&taskThree.isDone()){
//代处理完后统一返回结果集
break;
}
Thread.sleep(10000);
}
}
3.SpringBoot使用@Async实现异步调用-自定义线程池
使用@EnableAsync+@Async默认实现SimpleAsyncTaskExecutor 不是真的线程池,这个类不重用线程,每次调用 都会创建一个新的线程,非常的消耗资源。
(1)注入线程池对象
package com.yh;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@SpringBootApplication
public class BootAsyncApplication {
public static void main(String[] args) {
SpringApplication.run(BootAsyncApplication.class, args);
}
@Bean
public Executor myTaskExecutor(){
ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor();
//创建线程池,并初始化线程数量
executor.setCorePoolSize(10);
//最大线程数
executor.setMaxPoolSize(15);
//用来缓冲执行任务的队列
executor.setQueueCapacity(200);
//当超过核心先线程数之外的线程在空闲时间到达之后就会被销毁
executor.setKeepAliveSeconds(60);
//设置好之后可以方便定位处理任务所在的线程池
executor.setThreadNamePrefix("myTask");
//用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
executor.setWaitForTasksToCompleteOnShutdown(true);
//该方法用来设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
executor.setAwaitTerminationSeconds(60);
//线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
package com.yh.task;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import java.util.Random;
import java.util.concurrent.Future;
/**
* @Author youHui
**/
@Component
public class AsyncTask {
/**
* 这是同步
*/
public static Random random=new Random();
@Async("myTaskExecutor")
public Future<String> doTaskOne() throws Exception{
System.out.println("开始任务一");
long startTime=System.currentTimeMillis();//当前时间
Thread.sleep(random.nextInt(100000));
long endTime=System.currentTimeMillis();
System.out.println("完成任务一消耗的时间:"+(endTime-startTime)+"毫秒");
return new AsyncResult<>("任务一已完成");
}
@Async("myTaskExecutor")
public Future<String> doTaskTwo() throws Exception{
System.out.println("开始任务二");
long startTime=System.currentTimeMillis();//当前时间
Thread.sleep(random.nextInt(100000));
long endTime=System.currentTimeMillis();
System.out.println("完成任务二消耗的时间:"+(endTime-startTime)+"毫秒");
return new AsyncResult<>("任务一已完成");
}
@Async("myTaskExecutor")
public Future<String> doTaskThree() throws Exception{
System.out.println("开始任务三");
long startTime=System.currentTimeMillis();//当前时间
Thread.sleep(random.nextInt(100000));
long endTime=System.currentTimeMillis();
System.out.println("完成任务三消耗的时间:"+(endTime-startTime)+"毫秒");
return new AsyncResult<>("任务一已完成");
}
}
@Test
public void testTask() throws Exception{
Future<String> taskOne=asyncTask.doTaskOne();
Future<String> taskTwo=asyncTask.doTaskTwo();
Future<String> taskThree=asyncTask.doTaskThree();
while (true){
if(taskOne.isDone()&&taskTwo.isDone()&&taskThree.isDone()){
//代处理完后统一返回结果集
break;
}
Thread.sleep(10000);
}
}
十二、SpringBoot权限框架Shiro
subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。
security Manager:安全管理器,主体进行认证和授权都是通过securityManager进行。
authenticator:认证器,主体进行认证最终通过authenticator进行的。
authorizer:授权器,主体进行授权最终通过authorizer进行的。
sessionManager:web应用中一般是用web容器对session进行管理,shiro也提供一套session管理的方式。
SessionDao: 通过SessionDao管理session数据,针对个性化的session数据存储需要使用sessionDao。
cache Manager:缓存管理器,主要对session和授权数据进行缓存,比如将授权数据通过cacheManager进行缓存管理,和
ehcache整合对缓存数据进行管理。
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储。
realm:域,领域,相当于数据源,通过realm存取认证、授权相关数据。
记住一点,Shiro 不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro 即可。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
(2)用户认证
public void authentication(){
/*****************安全管理器环境**************************/
//构建安全管理器环境SecurityManager环境
DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
//创建数据源
SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm();
simpleAccountRealm.addAccount("zhangwuji","666666");
//设置到安全管理器
defaultWebSecurityManager.setRealm(simpleAccountRealm);
//把安全管理器配置到工具包里
SecurityUtils.setSecurityManager(defaultWebSecurityManager);
/*****************安全管理器环境**************************/
/*****************主体提交验证**************************/
//获取主体
Subject subject = SecurityUtils.getSubject();
//创建主体提交时需要携带的token(由用户名密码组成)
UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken("zhangwuji","666666");
try {
//主体提交验证
subject.login(usernamePasswordToken);
/*****************主体提交验证**************************/
//验证通过返回true,不通过返回false
System.out.println("用户认证的状态"+subject.isAuthenticated());
//退出
subject.logout();
System.out.println("用户认证的状态"+subject.isAuthenticated());
}catch (UnknownAccountException uae){
System.out.println("用户不存在");
}catch (IncorrectCredentialsException ice){
System.out.println("用户名密码不匹配");
}catch (LockedAccountException lae){
System.out.println("账号已经被锁定");
}catch (AuthenticationException ae){
System.out.println("用户名密码不匹配");
}
}
(2)用户授权
public void authorization(){
/*****************安全管理器环境**************************/
//构建安全管理器环境SecurityManager环境
DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
//创建数据源
SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm();
simpleAccountRealm.addAccount("zhangwuji","666666","admin","test");//更具角色授权
//设置到安全管理器
defaultWebSecurityManager.setRealm(simpleAccountRealm);
//把安全管理器配置到工具包里
SecurityUtils.setSecurityManager(defaultWebSecurityManager);
/*****************安全管理器环境**************************/
/*****************主体提交验证**************************/
//获取主体
Subject subject = SecurityUtils.getSubject();
//创建主体提交时需要携带的token(由用户名密码组成)
UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken("zhangwuji","666666");
try {
//主体提交验证
subject.login(usernamePasswordToken);
/*****************主体提交验证**************************/
//验证通过返回true,不通过返回false
System.out.println("用户认证的状态"+subject.isAuthenticated());
//判断该用户是否拥有这个角色
subject.checkRoles("admin","user");
//退出
subject.logout();
System.out.println("用户认证的状态"+subject.isAuthenticated());
}catch (UnknownAccountException uae){
System.out.println("用户不存在");
}catch (IncorrectCredentialsException ice){
System.out.println("用户名密码不匹配");
}catch (LockedAccountException lae){
System.out.println("账号已经被锁定");
}catch (AuthenticationException ae){
System.out.println("用户名密码不匹配");
}catch (UnauthorizedException e){
System.out.println("该用户没有该权限访问");
}
}
(3)SpringBoot使用IniRealm进行认证授权
SimpleAccountRealm写死了用户数据,不够灵活,IniRealm是Shiro提供一种Realm实现。用户、角色、权限等信息集中在一个.ini文件里。
①配置.ini文件
在resources目录下创建一个shiro.ini文件(角色的配置文件)
[users] ----->用户的角色
test=666666,test
admin=66666,admin
[roles]------->用户的权限
test=user:list,user:edit
admin=*
②读取角色信息并认证授权
public void testIniRealm(){
DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
//读取配置文件的内容
IniRealm iniRealm=new IniRealm("classpath:shiro.ini");
defaultWebSecurityManager.setRealm(iniRealm);
SecurityUtils.setSecurityManager(defaultWebSecurityManager);
/*****************主体提交验证**************************/
//获取主体
Subject subject = SecurityUtils.getSubject();
//创建主体提交时需要携带的token(由用户名密码组成)
UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken("test","666666");
try {
//主体提交验证
subject.login(usernamePasswordToken);
/*****************主体提交验证**************************/
//验证通过返回true,不通过返回false
System.out.println("用户认证的状态"+subject.isAuthenticated());
//判断该用户是否拥有这个角色
subject.checkRoles("test");
//校验是否拥有其权限
subject.checkPermissions("user:list");//是否拥有用户列表的权限
//退出
subject.logout();
System.out.println("用户认证的状态"+subject.isAuthenticated());
}catch (UnknownAccountException uae){
System.out.println("用户不存在");
}catch (IncorrectCredentialsException ice){
System.out.println("用户名密码不匹配");
}catch (LockedAccountException lae){
System.out.println("账号已经被锁定");
}catch (AuthenticationException ae){
System.out.println("用户名密码不匹配");
}catch (UnauthorizedException e){
System.out.println("该用户没有该权限访问");
}
}
(4)SpringBoot使用JdbcRealm进行认证授权
IniRealm虽然可以通过.ini文件进行配置然后读取其安全信息,但还是得提前写好相关信息,还是不够灵活,JdbcRealm可以直接从db中读取信息,然后再认证授权认证。
①加入数据库相关依赖
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
②用户权限信息
③认证授权
public void testJdbcRealm(){
//配置数据源
DruidDataSource druidDataSource=new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://localhost:3306/shiro");
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUsername("root");
druidDataSource.setPassword("666666");
//配置文件中的用户权限信息
JdbcRealm jdbcRealm=new JdbcRealm();
jdbcRealm.setDataSource(druidDataSource);
//开启权限开关
jdbcRealm.setPermissionsLookupEnabled(true);
//构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager=new DefaultWebSecurityManager();
//设置Realm
defaultSecurityManager.setRealm(jdbcRealm);
SecurityUtils.setSecurityManager(defaultSecurityManager);
/*****************主体提交验证**************************/
//获取主体
Subject subject = SecurityUtils.getSubject();
//创建主体提交时需要携带的token(由用户名密码组成)
UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken("test","123456");
try {
//主体提交验证
subject.login(usernamePasswordToken);
/*****************主体提交验证**************************/
//验证通过返回true,不通过返回false
System.out.println("用户认证的状态"+subject.isAuthenticated());
//判断该用户是否拥有这个角色
subject.checkRoles("test");
//校验是否拥有其权限
subject.checkPermissions("user:deleted","role:list");//是否拥有用户列表的权限
//退出
subject.logout();
System.out.println("用户认证的状态"+subject.isAuthenticated());
}catch (UnknownAccountException uae){
System.out.println("用户不存在");
}catch (IncorrectCredentialsException ice){
System.out.println("用户名密码不匹配");
}catch (LockedAccountException lae){
System.out.println("账号已经被锁定");
}catch (AuthenticationException ae){
System.out.println("用户名密码不匹配");
}catch (UnauthorizedException e){
System.out.println("该用户没有该权限访问");
}
}
(5)Springboot使用自定义Realm进行认证授权
JdbcRealm虽然能从数据库读取用户授权信息,但其底层已经固定写好了sql语句,一旦数据库名或表名发生改变有得重新改写底层sql这样一来就造成不灵活,自定义Realm可以解决这个问题。
Realm只需要继承:AuthorizingRealm重写doGetAuthenticationfo(用户认证)、doGetAuthorizationInfo(用户授权)这两个方法即可。
①自定义Realm
package com.yh.shiro;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.util.StringUtils;
import javax.swing.text.SimpleAttributeSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Author youHui
**/
public class CustomRealm extends AuthorizingRealm {
/**
* mock用户信息
* @param principalCollection
* @return
*/
/************************数据*************************/
private Map<String,String> userMap=new HashMap<>();
{
userMap.put("admin","123456");
userMap.put("test","123456");
}
/************************数据*************************/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//用户授权
String username = (String) principalCollection.getPrimaryPrincipal();
//获取角色信息
List<String> roles=getRolesByUserName(username);
List<String> permissions=getPermissionsByUsername(username);
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.addRoles(roles);
info.addStringPermissions(permissions);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户数据
String username = (String) authenticationToken.getPrincipal();
String password = getPasswordByUsername(username);
if(StringUtils.isEmpty(password)){
return null;
}
//用户认证
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(username,password,getName());
return info;
}
//通过用户名获取到密码
private String getPasswordByUsername(String username){
return userMap.get(username);
}
//通过用户名获取角色信息
private List<String> getRolesByUserName(String username){
List<String> roles=new ArrayList<>();
if(username.equals("admin")){
roles.add("admin");
}else {
roles.add("test");
}
return roles;
}
//通过用户名获取权限信息
private List<String> getPermissionsByUsername(String username){
List<String> permissions=new ArrayList<>();
if(username.equals("admin")){
permissions.add("*");//权限为所有
}
permissions.add("user:list");
permissions.add("user:deleted");
permissions.add("user:edit");
return permissions;
}
}
②认证授权
public void testCustomRealm(){
DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
//创建域
CustomRealm customRealm=new CustomRealm();
defaultWebSecurityManager.setRealm(customRealm);
SecurityUtils.setSecurityManager(defaultWebSecurityManager);
/*****************主体提交验证**************************/
//获取主体
Subject subject = SecurityUtils.getSubject();
//创建主体提交时需要携带的token(由用户名密码组成)
UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken("test","123456");
try {
//主体提交验证
subject.login(usernamePasswordToken);
/*****************主体提交验证**************************/
//验证通过返回true,不通过返回false
System.out.println("用户认证的状态"+subject.isAuthenticated());
//判断该用户是否拥有这个角色
subject.checkRoles("test");
//校验是否拥有其权限
subject.checkPermissions("user:deleted","role:list");//是否拥有用户列表的权限
//退出
subject.logout();
System.out.println("用户认证的状态"+subject.isAuthenticated());
}catch (UnknownAccountException uae){
System.out.println("用户不存在");
}catch (IncorrectCredentialsException ice){
System.out.println("用户名密码不匹配");
}catch (LockedAccountException lae){
System.out.println("账号已经被锁定");
}catch (AuthenticationException ae){
System.out.println("用户名密码不匹配");
}catch (UnauthorizedException e){
System.out.println("该用户没有该权限访问");
}
}
(6)SpringBoot整合shiro——盐值加密
①自定义Realm
package com.yh.shiro;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.util.StringUtils;
import javax.swing.text.SimpleAttributeSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Author youHui
**/
public class CustomRealm extends AuthorizingRealm {
/**
* mock用户信息
* @param principalCollection
* @return
*/
/************************数据*************************/
private Map<String,String> userMap=new HashMap<>();
{
userMap.put("admin","123456");
userMap.put("test","123456");
}
/************************数据*************************/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//用户授权
String username = (String) principalCollection.getPrimaryPrincipal();
//获取角色信息
List<String> roles=getRolesByUserName(username);
List<String> permissions=getPermissionsByUsername(username);
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.addRoles(roles);
info.addStringPermissions(permissions);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
//获取用户数据
String username = (String) authenticationToken.getPrincipal();
String password = getPasswordByUsername(username);
if(StringUtils.isEmpty(password)){
return null;
}
//用户认证
// info=new SimpleAuthenticationInfo(username,password,getName());
//SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(username,getEncPassword(password),getName());
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(
username,getEncPassword(password,username),getName());
//设置盐值
info.setCredentialsSalt(ByteSource.Util.bytes(username));
return info;
}
//通过用户名获取到密码
private String getPasswordByUsername(String username){
return userMap.get(username);
}
//通过用户名获取角色信息
private List<String> getRolesByUserName(String username){
List<String> roles=new ArrayList<>();
if(username.equals("admin")){
roles.add("admin");
}else {
roles.add("test");
}
return roles;
}
//通过用户名获取权限信息
private List<String> getPermissionsByUsername(String username){
List<String> permissions=new ArrayList<>();
if(username.equals("admin")){
permissions.add("*");//权限为所有
}
permissions.add("user:list");
permissions.add("user:deleted");
permissions.add("user:edit");
return permissions;
}
//返回密文密码
private String getEncPassword(String password,String salt){
return new Md5Hash(password,salt,3).toString();
}
//private String getEncPassword(String password){
//return new Md5Hash(password,null,3).toString();
//}
}
②认证授权
public void testSaltMatcher(){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
CustomRealm customRealm = new CustomRealm();
//指定解密得方式
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");//md5加密
matcher.setHashIterations(3);//加密得次数
//配置到域中
customRealm.setCredentialsMatcher(matcher);
defaultWebSecurityManager.setRealm(customRealm);
SecurityUtils.setSecurityManager(defaultWebSecurityManager);
/*****************主体提交验证**************************/
//获取主体
Subject subject = SecurityUtils.getSubject();
//创建主体提交时需要携带的token(由用户名密码组成)
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("test", "123456");
try {
//主体提交验证
subject.login(usernamePasswordToken);
/*****************主体提交验证**************************/
//验证通过返回true,不通过返回false
System.out.println("用户认证的状态" + subject.isAuthenticated());
//判断该用户是否拥有这个角色
subject.checkRoles("test");
//校验是否拥有其权限
subject.checkPermissions("user:deleted", "role:list");//是否拥有用户列表的权限
//退出
subject.logout();
System.out.println("用户认证的状态" + subject.isAuthenticated());
} catch (UnknownAccountException uae) {
System.out.println("用户不存在");
} catch (IncorrectCredentialsException ice) {
System.out.println("用户名密码不匹配");
} catch (LockedAccountException lae) {
System.out.println("账号已经被锁定");
} catch (AuthenticationException ae) {
System.out.println("用户名密码不匹配");
} catch (UnauthorizedException e) {
System.out.println("该用户没有该权限访问");
}
}