当前位置: 首页 > news >正文

springboot笔记

微服务架构

微服务是一种架构风格,开发构建应用的时候把应用的业务构建成一个个的小服务(这就类似于把我们的应用程序构建成了一个个小小的盒子,它们在一个大的容器中运行,这种一个个的小盒子我们把它叫做服务),通过服务的组合来完成具体的业务。服务之间相互独立,互不影响

第一个springboot程序

环境:

springboot2.7.4

maven3.6.2

初始化项目

通过idea集成工具初始化springboot

1.new一个新的项目,选择spring初始化,选择官方地址(这里我们也可以使用下面的custom填入alibaba的地址来初始化),点击next

在这里插入图片描述

2.填写组名和项目名,选择jdk版本为8,删除多余的包路径(避免包路径过于复杂),点击next

在这里插入图片描述

3.初始化导入依赖,这里导入两个(也可以什么都不用导入,后期我们手动导入),web和devtools(热部署工具,每次修改不同重新启动服务器)。左上角直接搜索devtools即可,点击next

在这里插入图片描述

4.最后一步点击finish,至此项目初始化完毕

@ResponseBody的使用(返回json字符串)

@ResponseBody是在标注了@Controller的类中使用的,它本身标注在类的方法上,代表该类的这个方法只返回json字符串,不经过视图解析进行视图跳转。和@RestController不同的是,它可以使用在控制视图跳转的类(标注了@Controller的类)中,只标注一个方法不进入视图解析器。

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
public class HelloController {@RequestMapping("/hello")@ResponseBodypublic String hello() {return "hello,world!";}
}

修改默认端口号

在application.properties文件中或者application.yml文件中

# 修改项目的端口号
server.port=8081

修改启动默认logo

因为springboot的默认配置。默认配置中存在一个路径resource/banner.txt,如果该路径下的banner.txt文件存在,则启动logo显示为该文件内容。如果不存在,则显示默认logo。所以我们只需要新建一个文件banner.txt就可以替换logo

springboot自动装配原理

springboot项目都是基于自动配置

springboot的版本管理

在springboot项目中,我们导入的依赖不需要版本号,这是因为所有的版本都在springboot项目中的父依赖管理了,springboot的父依赖规划好了所有我们可能会用到的jar包的版本。选择了springboot的版本也就是选择了之后所有用到jar包的版本。springboot核心依赖都在父工程中

springboot启动器

所有带有starter的依赖都是springboot的启动器。启动器其实就是springboot把我们实际会用到的的项目的一些场景给抽象出来了。比如web启动器,就是给web环境准备的,它会把web需要用到的jar打包进入一个依赖(这里依赖就是启动器),我们只需要导入这个springboot打包好的依赖,就可以直接进行相关场景的开发。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId>
</dependency>

springboot主程序

就像一个普通的java程序一样,springboot也就一个主程序入口。这个入口用来启动我们整个springboot项目

//标注一个springboot应用程序
@SpringBootApplication
public class Springboot01HelloworldApplication {public static void main(String[] args) {SpringApplication.run(Springboot01HelloworldApplication.class, args);}}

@SpringBootApplication内部原理

这个注解的内部其实也有一个个的其他注解组成。

@SpringBootConfiguration : springboot配置类@Configuration : spring配置类@Component : 组件
@EnableAutoConfiguration : 启用自动配置@AutoConfigurationPackage : 自动配置包@Import({AutoConfigurationImportSelector.class}) : 自动配置包注册@Import({AutoConfigurationImportSelector.class}) : 导入选择器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9FIxWVsv-1677311559169)(C:\Users\zhongzheng\Desktop\笔记\JavaWeb\springboot\image-20221015110633289.png)]

yaml格式的配置文件

application.yaml非配置的写法

# 1.可以写kv键值对
name: zhong# 2.对象
student:name: zhongage: 3# 3.对象的行内写法
student2: {name: zhong, age: 4}# 4.数组
pets:- dog- cat- pig# 5.数组的行内写法
pets2: [cat, dog, pig]

yaml给实体类赋值@ConfigurationProperties

首先在application.yaml中准备好数据

person:name: zhongage: 3happy: falsebirth: 2000/1/1maps: {k1: va, k2: v2}list:- code- music- girldog:name: 旺财age: 3

在实体类中加上注解@ConfigurationProperties(prefix = “person”),括号内的是指定需要给实体类绑定yaml文件中的哪个数据。加上注解之后会爆红提示我们配置,但是这个配置可有可无,它并不影响程序的正常运行,如需解决爆红,在pom.xml文件中加入依赖

JSR303校验

@Validated在类上标注,意思是开启数据校验。然后通过在属性上添加注解来实现校验。

多环境配置和配置文件的位置

配置文件可以在四个位置被检测到

优先级:项目/config > 项目 > resource/config > resource

首先第二套环境配置文件名为application-dev.yaml

# springboot 的多环境配置可以选择激化哪一个环境
spring:profiles:active: dev

yaml存在多文档格式

server:port: 8081
spring:profiles:active: test#多文档模式,使用 --- 分隔开
---server:port: 8082
# 给第二套环境命名,此方式已经弃用
spring:profiles: test
# 在2.4之后的版本中推荐使用
spring:config:activate:on-profile: dev

自动配置原理理解

AutoConfiguration注解去装载一个XXXproperties.java文件,这个文件又去通过注解加载了yml文件中我们自定义的一些配置。那么如果我们yml中的配置存在,XXXproperties文件生效。自动装配去找到这个文件并且把它加载进来实现加载我们的自定义配置。如果不存在自定义配置,那么XXXproperties就会失效,AutoConfiguration只会加载它自己已经定义好的一些默认值配置

总结

springboot启动会加载大量的配置类

看我们需要的功能在springboot默认写好的配置类中是否存在

配置类中存在哪些组件(只要我们要用的组件存在,我们就不需要手动配置)

给容器中自动配置类添加组件的时候,会从xxproperties类中获取某些属性,我们需要在配置文件中指定这些属性的值就好

springboot的web开发

导入静态资源

可以使用maven的方式导入前端的一些包(以webjar的方式)

<dependency><groupId>org.webjars</groupId><artifactId>jquery</artifactId><version>3.4.1</version>
</dependency>

resource下的这些目录的内容都可以被直接访问

优先级: resource > static > public

制作首页

springboot有自己的一个首页的配置类,它指定了一个在resource目录下的index首页文件会被进入项目于时加载,如果我们没有指定他就会加载配置好的一个默认的首页。

我们在resource目录的static目录下新建index.html文件。启动项目即可直接加载该文件

在templates目录下的所有资源,只能够通过controller跳转或访问。类似于jsp开发中是WEB-INF目录。如果是需要跳转到templates目录下的页面,需要模板引擎的支持

图标定制

<link rel="icon" th:href="@{/public/favicon.ico}" type="image/x-icon"/>

模板引擎Thymeleaf

导入依赖

<dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>

模板写在templates目录下

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<!--:th:test表示thymeleaf接管此标签,并且绑定标签中的某个属性-->
<div th:text="${msg}"></div></body>
</html>

html文件需要使用模板引擎要导入约束

<html lang="en" xmlns::th="http://www.thymeleaf.org">

thymeleaf语法

<!--utext表示接收的值不会被转义,也就是不会被全部识别成字符串-->
<div th:text="${msg}"></div>
<!--正常模式下的text绑定会被识别成字符串的形式例如<h1>标签也会被页面当成普通字符处理-->
<div th:text="${msg}"></div>
<!--遍历后端传递过来的list挨个取出里面的值-->
<div th:each="user:${userList}" th:text="${user}"></div>
<!--行内写法,通过两个中括号中间加入${}来取值-->
<div th:each="user:${userList}">[[${user}]]</div>

装配扩展springMVC

自定义配置,首先新建目录config,再新建配置类MyMvcConfig

package com.zhong.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;//这个类用来扩展mvc
@Configuration
//@EnableWebMvc 如果需要全面自定义mvc配置,不想用自动配置的需要加上这个注解
public class MyMvcConfig implements WebMvcConfigurer {/*重写视图跳转*/@Overridepublic void addViewControllers(ViewControllerRegistry registry) {/*注册一个视图控制器,请求/zhong路径的请求将会返回一个名字为test的视图*//*这可能是controller注解的mvc底层实现*/registry.addViewController("/zhong").setViewName("tset");}
}

根目录下的请求都推荐使用这种sprimvc的视图跳转方式配置

package com.zhong.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;//这个类用来扩展mvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");registry.addViewController("/index").setViewName("index");registry.addViewController("/index.html").setViewName("index");}
}

thymeleaf的路径写法使用@{/} 这里/代表项目根路径,下面的静态资源目录不用写,直接使用它们内部的目录,静态资源都使用thymeleaf接管

配置项目根路径

server:servlet:context-path: /zhong

国际化

在资源目录下新建目录i18n(国际化英文单词的缩写)。新建两个文件login.properties,login_zh__CN.properties,新建完成后ide会自动合并两个文件,将它们放在同一个目录下。后面需要加入其他的语言包可以直接在这个ide生成的目录右键add

在idea主界面下方有个选项Resource Bundle可以进行可视化配置,配置好我们的语言包之后,需要在springboot配置文件中配置

# 配置文件的真实位置
spring:messages:basename: i18n.login

然后在html需要使用到语言包的位置上使用#{}取值

扩展本地解析器来做语言包之间的切换,我们可以通过请求中的lang属性来指定返回的语言。在@{}路径中使用()来传递参数

<a class="btn btn-sm" th:href="@{/index(l=zh_CH)}">中文</a>
<a class="btn btn-sm" th:href="@{/index(l=en_US)}">English</a>

在html切换语言按钮中请求连接在config下新建MyLocaleResolver

package com.zhong.config;import org.springframework.web.servlet.LocaleResolver;
import org.thymeleaf.util.StringUtils;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;public class MyLocaleResolver implements LocaleResolver {//解析请求@Overridepublic Locale resolveLocale(HttpServletRequest request) {//获取请求中的语言参数String language = request.getParameter("l");Locale locale = Locale.getDefault();//取得默认的if (!StringUtils.isEmpty(language)) {String[] split = language.split("_");locale =  new Locale(split[0], split[1]);}return locale;}@Overridepublic void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {}
}

把它配置到springmvc中让spring接管

@Bean
public LocaleResolver localeResolver() {return new MyLocaleResolver();
}

项目实战

登陆功能实现

LoginController

package com.zhong.controller;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.thymeleaf.util.StringUtils;@Controller
public class LoginController {@RequestMapping("/user/login")public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model) {//登陆业务if (!StringUtils.isEmpty(username) && "123456".equals(password)) {return "redirect:/main";}else {model.addAttribute("loginError", "用户名或密码错误");return "index";}}}

MyMvcConfig

package com.zhong.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;//这个类用来扩展mvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");registry.addViewController("/index").setViewName("index");registry.addViewController("/index.html").setViewName("index");registry.addViewController("/main.html").setViewName("dashboard");registry.addViewController("/main").setViewName("dashboard");}@Beanpublic LocaleResolver localeResolver() {return new MyLocaleResolver();}
}

拦截器

新建类实现拦截器接口LoginHandlerInterceptor

package com.zhong.config;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class LoginHandlerInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Object loginUser = request.getSession().getAttribute("loginUser");if (loginUser==null) {
//            System.out.println("======================================================2121212");request.setAttribute("loginError", "请先登录");request.getRequestDispatcher("/index").forward(request, response);return false;}else {return true;}}}

LoginController

package com.zhong.controller;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.thymeleaf.util.StringUtils;import javax.servlet.http.HttpSession;@Controller
public class LoginController {@RequestMapping("/user/login")public String login(@RequestParam("username") String username,@RequestParam("password") String password,Model model,HttpSession session) {//登陆业务if (!StringUtils.isEmpty(username) && "123456".equals(password)) {session.setAttribute("loginUser", username);return "redirect:/main";}else {model.addAttribute("loginError", "用户名或密码错误");return "index";}}}

MyMvcConfig

package com.zhong.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;//这个类用来扩展mvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");registry.addViewController("/index").setViewName("index");registry.addViewController("/index.html").setViewName("index");registry.addViewController("/main.html").setViewName("dashboard");registry.addViewController("/main").setViewName("dashboard");}@Overridepublic void addInterceptors(InterceptorRegistry registry) {/*配置拦截器*/registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/index","/","/user/login","/asserts/css/*","/asserts/js/**","/asserts/img/**");}
}

thymeleaf抽取公共页面

使用th:fragment="名字"可以抽取出这部分的代码作为一个类似插件的部分

在需要插入的地方写上

<!--th:insert的意思是将抽取出来的部分插入到div里面作为子元素-->
<div th:insert="~{dashboard::topbar}"></div>

需要使用~{抽取出插件的页面名字::插件名字},同时这种写法也可使用()传递参数,()传递参数就相当于以前的jsp?传递参数

在动态设置导航栏激活状态的时候,可以使用表达式加上三元运算符来判断激活状态

<!-- 接收参数并且判断-->
<a th:class="${active=='main.html'?'nav-link active':'nav-link'}" th:href="@{/main}"></a>
<!--传递参数-->
<div th:insert="~{commons/commons::sidebar(active='list.html')}"></div>

页面操作

<thead><tr><th>id</th><th>lastName</th><th>email</th><th>sex</th><th>department</th><th>birth</th><th>操作</th></tr>
</thead>
<tbody><tr th:each="emp:${emps}"><td th:text="${emp.getId()}"></td><td>[[ ${emp.getLastName()} ]]</td><td th:text="${emp.getEmail()}"></td><td th:text="${emp.getSex()==0?'女':'男'}"></td><td th:text="${emp.getDepartment().getDepartmentName()}"></td><td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td><td><button class="btn btn-sm btn-primary">编辑</button><button class="btn btn-sm btn-danger">删除</button></td></tr>
</tbody>

404页面定制

springboot帮助我们配置好的存放错误页面的目录和导向错误页面的路由。我们只需要在templates目录下新建error目录,在该目录下新建名字为404或者500的页面即可

spring Data整合JDBC

导入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency>

配置数据源,这里可能需要设置时区,但是在my.ini里配置过就不需要设置

spring:datasource:username: rootpassword: 123456url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&&characterEncoding=utf-8&&useSSL=falsedriver-class-name: com.mysql.jdbc.Driver

原始的就是在需要使用JDBC是类中自动装配DataSource就可以使用,但是一般我们不这样用,我们会用springboot帮我们写好的bean

@Autowired
DataSource dataSource;@Test
void contextLoads() throws SQLException {//默认数据源System.out.println(dataSource.getClass());Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();
}

这里介绍一种springboot写好的bean模板叫做 XXXtemplate,例如springboot写好的jdbc的bean就叫做jdbc template,使用它可以直接连接数据库。并且这个被springboot封装过的jdbc做好了事务,它会帮我们自动提交事务。

package com.zhong.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.List;
import java.util.Map;@RestController
public class JDBCController {@Autowiredprivate JdbcTemplate jdbcTemplate;@GetMapping("/userList")public List<Map<String, Object>> userList() {String sql = "select * from mybatis.user";List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);return maps;}@GetMapping("/addUser")public String addUser() {String sql = "insert into mybatis.user(id, name, pwd) values(5, 'xxx', '123456')";int update = jdbcTemplate.update(sql);return "addUserok";}@GetMapping("/updateUser/{id}")public String updateUser(@PathVariable int id) {String sql = "update mybatis.user set name = ?,pwd = ? where id = ?;";Object[] objects = new Object[3];objects[0] = "aaa";objects[1] = "123";objects[2] = "5";int update = jdbcTemplate.update(sql, objects);return "updateUserok";}@GetMapping("/deleteUser/{id}")public String deleteUser(@PathVariable int id) {String sql = "delete from mybatis.user where id = ?";int update = jdbcTemplate.update(sql, id);return "deleteUserok";}
}

整合Druid数据源

导入依赖

<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.14</version>
</dependency>

通过type指定springboot的数据源

spring:datasource:username: rootpassword: 123456url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&&characterEncoding=utf-8&&useSSL=falsedriver-class-name: com.mysql.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource#durid数据源专有配置initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: select 1 from dualtestWileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: true#配置监控统计的拦截的filter,stat:监控统计、log4j:日志记录、wall:防御sql注入#如果运行时报错 类找不到异常:log4j#就导入log4j依赖filters: stat,wall,log4jmaxPoolPreparedStatementPerConnectionSize: 20userGlobalDataSourceStat: trueconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

新建目录config,新建类DruidConfig

package com.zhong.config;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;
import java.util.HashMap;//标注这个类是一个配置类
@Configuration
public class DruidConfig {//将yml文件中配置的属性绑定进来@ConfigurationProperties(prefix = "spring.datasource")@Bean //将这个new出来的对象托管给springpublic DataSource druidDataSource() {return new DruidDataSource();}//后台监控//ServletRegistrationBean意思是帮助我们把一些druid的插件注册成bean@Bean //需要注册为beanpublic ServletRegistrationBean statViewServlet() {//new一个bean的注册,参数传递一些插件,同时指定路径,进入该路径的时候会进入该插件的图形界面ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");//监控后台需要登陆HashMap<String, String> initParameters = new HashMap<>();//增加配置initParameters.put("loginUsername","admin");//loginUsername固定写法initParameters.put("loginPassword","123456");//固定写法//允许访问initParameters.put("allow", "");//如果该属性为空,则代表所有人可以访问//禁止访问
//        initParameters.put("kuangsheng", "这里写ip地址");//只要这样配置之后,这个ip地址就会被禁止访问监控后台bean.setInitParameters(initParameters);//设置初始化参数return bean;}//注册filter@Beanpublic FilterRegistrationBean webStatFilter() {FilterRegistrationBean bean = new FilterRegistrationBean();bean.setFilter(new WebStatFilter());HashMap<String, String> initParameters = new HashMap<>();//对请求这些资源或者路劲的请求不统计initParameters.put("exclusions", "*.js,*.css,/druid/*");bean.setInitParameters(initParameters);return bean;}
}

整合mybatis框架

导入依赖

<dependencies><!--非官方写的启动器会以框架名字开头--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><!--官方启动器则会以spring-boot开头--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--老版驱动<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency>--><!--新版驱动--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>

配置数据源

spring:datasource:url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&&characterEncoding=utf-8&&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver# springboot整合mybatis就是接管了mybatis的核心配置文件
mybatis:# 我们需要在这里配置好mybatis的别名映射type-aliases-package: com.zhong.pojo# 配置接口实现xml文件的位置mapper-locations: classpath:mybatis/mapper/*.xml

编写实体类

package com.zhong.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;@Data
@AllArgsConstructor
@ToString
@NoArgsConstructor
public class User {private int id;private String name;private String pwd;
}

编写Mapper接口

package com.zhong.mapper;import com.zhong.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.stereotype.Repository;import java.util.List;@Mapper//表示这个类是一个mybaits的一个mapper
//@MapperScan("com.zhong.mapper")或者在主启动类上添加mapper包扫描
@Repository//在spring中表示这是一个dao层对象
public interface UserMapper {List<User> queryUserList();User queryUserById(int id);int addUser(User user);int updateUser(User user);int deleteUserById(int id);
}

在resource下编写mapper的实现

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhong.mapper.UserMapper"><select id="queryUserList" resultType="user">select * from user;</select><select id="queryUserById" resultType="user" parameterType="_int">select * from user where id = #{id};</select><insert id="addUser" parameterType="user">insert into user(id,name,pwd) values(#{id},#{name},#{pwd});</insert><update id="updateUser" parameterType="user">update user set name = #{name}, pwd = #{pwd} where id = #{id};</update><delete id="deleteUserById" parameterType="_int">delete from user where id = #{id};</delete>
</mapper>

编写controller测试一下

package com.zhong.controller;import com.zhong.mapper.UserMapper;
import com.zhong.pojo.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.List;@RestController
public class UserController {@Resourceprivate UserMapper userMapper;@GetMapping("/queryUserList")public String queryUserList() {List<User> userList = userMapper.queryUserList();return userList.toString();}}

SpringSecurity(安全)

这是一个权限认证框架,如果不使用它,我们用过滤器拦截器一样能够完成权限认证的功能。

导入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

编写配置类

方式一,web安全适配器(已启用)

package com.zhong.config;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {//该方法是链式编程@Overrideprotected void configure(HttpSecurity http) throws Exception {//实现首页所有人可以访问,功能页只有对应的人可以访问//认证请求http.authorizeRequests().antMatchers("/").permitAll()  //对某个路径下的所有请求都允许访问.antMatchers("/level1/**").hasRole("vip1") //对该路径下的只有设定好的角色才可以访问.antMatchers("/level2/**").hasRole("vip2").antMatchers("/level3/**").hasRole("vip3");//配置如果访问没有权限,默认跳转到登录页http.formLogin();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//从内存拿到数据来进行验证//这里需要配置一个密码加密方式,否则会跑不起来auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())//配置认证的用户,用户角色可以配置多个.withUser("zhong").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")//可以通过and配置多个认证用户.and().withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3").and().withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");}
}

方式二

新方式不需要继承WebSecurityConfigurerAdapter,而是注入一个过滤链的Bean,通过这个过滤链去处理用户登录的请求

package com.zhong.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;@Configuration
public class SecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authz) -> authz.antMatchers("/").permitAll().antMatchers("/level1/**").hasRole("vip1").antMatchers("/level2/**").hasRole("vip2").antMatchers("/level3/**").hasRole("vip3")).formLogin();return http.build();}@Beanpublic InMemoryUserDetailsManager userDetailsService() {PasswordEncoder encoder = new BCryptPasswordEncoder();UserDetails user = User.withDefaultPasswordEncoder().username("root").password("123456").roles("vip1").build();//该设置密码加密的方式已经弃用,只用来写代码示例UserDetails user2 = User.withDefaultPasswordEncoder().username("zhong").password("123456").roles("vip1").build();return new InMemoryUserDetailsManager(user,user2);}}

注销功能实现

//开启注销功能,并清除cookie,清除session.但实际上一般不会这么干
//        http.logout().deleteCookies("remove").invalidateHttpSession(true);
//开启注销功能,并让它注销成功之后跳转到我们指定的路径
http.logout().logoutSuccessUrl("/");

权限显示控制

导入依赖

<dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId><version>3.0.4.RELEASE</version>
</dependency>

导入命名空间

在html文件中需要导入命名空间,方便使用代码提示。不导入程序一样能够跑起来。

<html lang="en" xmlns:th="http://www.thymeleaf.org"xmlns:sec="http://www.thymeleaf.org/extras/spring-security">

动态显示内容

<!--登录注销-->
<div class="right menu"><!--未登录--><div sec:authorize="!isAuthenticated()"><a class="item" th:href="@{/toLogin}"><i class="address card icon"></i> 登录</a></div><div sec:authorize="isAuthenticated()"><a class="item"><!--获取用户名-->用户名: <span sec:authentication="name"></span><!--获取用户角色-->角色: <span sec:authentication="authorities"></span></a></div><!--已登陆,显示用户名,注销--><div sec:authorize="isAuthenticated()"><!--注销--><a class="item" th:href="@{/logout}"><i class="sign-out icon"></i> 注销</a></div>
<!--动态菜单-->
<div class="column" sec:authorize="hasRole('vip1')">

跨域配置

//关闭默认开启的网站防御攻击,以此支持跨域请求
http.csrf().disable();

记住我和首页定制

开启记住我功能会保存用户的登陆状态,通过向用户电脑存放一个cookie来实现保存登陆状态,cookie有效期为两周

http.rememberMe();

定制登录页

//定制登录页,并且指定处理登陆请求的控制器。这里表单提交必须要是post
.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");

定制登录页的前提是,前端传递的参数必须是username和password,如果它们的name是其他的名称,框架识别不到就会登陆失败。当然,这种参数名字也可以定制,通过以下两种方法为参数指定一个新的接收前端参数的名字。

.formLogin().loginPage("/toLogin").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login");

同样的,记住我功能也可以定制参数

http.rememberMe().rememberMeParameter("remember");

Shiro

导入依赖

<?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"><parent><artifactId>springboot-08-shiro</artifactId><groupId>com.zhong</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>hello-shiro</artifactId><dependencies><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.10.0</version></dependency><!-- configure logging --><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>2.0.3</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.0</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.8.0-beta4</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.8.0-beta4</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency></dependencies></project>

日志配置

log4j.rootLogger=INFo,stdoutlog4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p 【%c 】 - ‰m %n#General Apache libraries
log4j.logger.org.apache=WARN#Spring
log4j.logger.org.springframework=WARN#Default Shiro Logging
log4j.logger.org.apache.shiro=INFO#Disable verbose Logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

shiro配置

[users]
# user 'root' with password 'secret' and the 'admin' role
root = secret, admin
# user 'guest' with the password 'guest' and the 'guest' role
guest = guest, guest
# user 'presidentskroob' with password '12345' ("That's the same combination on
# my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
darkhelmet = ludicrousspeed, darklord, schwartz
# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz# -----------------------------------------------------------------------------
# Roles with assigned permissions
# 
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
# -----------------------------------------------------------------------------
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
# license plate 'eagle5' (instance specific id)
goodguy = winnebago:drive:eagle5

快速启动

/** Licensed to the Apache Software Foundation (ASF) under one* or more contributor license agreements.  See the NOTICE file* distributed with this work for additional information* regarding copyright ownership.  The ASF licenses this file* to you under the Apache License, Version 2.0 (the* "License"); you may not use this file except in compliance* with the License.  You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing,* software distributed under the License is distributed on an* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY* KIND, either express or implied.  See the License for the* specific language governing permissions and limitations* under the License.*/import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** Simple Quickstart application showing how to use Shiro's API.** @since 0.9 RC2*/
public class Quickstart {private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);public static void main(String[] args) {// The easiest way to create a Shiro SecurityManager with configured// realms, users, roles and permissions is to use the simple INI config.// We'll do that by using a factory that can ingest a .ini file and// return a SecurityManager instance:// Use the shiro.ini file at the root of the classpath// (file: and url: prefixes load from files and urls respectively):DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();IniRealm iniRealm=new IniRealm("classpath:shiro.ini");defaultSecurityManager.setRealm(iniRealm);// for this simple example quickstart, make the SecurityManager// accessible as a JVM singleton.  Most applications wouldn't do this// and instead rely on their container configuration or web.xml for// webapps.  That is outside the scope of this simple quickstart, so// we'll just do the bare minimum so you can continue to get a feel// for things.SecurityUtils.setSecurityManager(defaultSecurityManager);// Now that a simple Shiro environment is set up, let's see what you can do:// get the currently executing user:Subject currentUser = SecurityUtils.getSubject();// Do some stuff with a Session (no need for a web or EJB container!!!)Session session = currentUser.getSession();session.setAttribute("someKey", "aValue");String value = (String) session.getAttribute("someKey");if (value.equals("aValue")) {log.info("Retrieved the correct value! [" + value + "]");}// let's login the current user so we can check against roles and permissions:if (!currentUser.isAuthenticated()) {UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");token.setRememberMe(true);try {currentUser.login(token);} catch (UnknownAccountException uae) {log.info("There is no user with username of " + token.getPrincipal());} catch (IncorrectCredentialsException ice) {log.info("Password for account " + token.getPrincipal() + " was incorrect!");} catch (LockedAccountException lae) {log.info("The account for username " + token.getPrincipal() + " is locked.  " +"Please contact your administrator to unlock it.");}// ... catch more exceptions here (maybe custom ones specific to your application?catch (AuthenticationException ae) {//unexpected condition?  error?}}//say who they are://print their identifying principal (in this case, a username):log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");//test a role:if (currentUser.hasRole("schwartz")) {log.info("May the Schwartz be with you!");} else {log.info("Hello, mere mortal.");}//test a typed permission (not instance-level)if (currentUser.isPermitted("lightsaber:wield")) {log.info("You may use a lightsaber ring.  Use it wisely.");} else {log.info("Sorry, lightsaber rings are for schwartz masters only.");}//a (very powerful) Instance Level permission:if (currentUser.isPermitted("winnebago:drive:eagle5")) {log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +"Here are the keys - have fun!");} else {log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");}//all done - log out!currentUser.logout();System.exit(0);}
}

springboot集成shiro

导入依赖

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.1</version>
</dependency>

编写配置类

package com.zhong.config;import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;public class UserRealm extends AuthorizingRealm {//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了授权");return null;}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("执行了认证");return null;}
}
package com.zhong.config;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class ShiroConfig {//shiroFilterFactoryBean@Beanpublic ShiroFilterFactoryBean getShiroFilterFactorBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();//设置安全管理bean.setSecurityManager(defaultWebSecurityManager);return bean;}//defaultWebSecurityManager@Bean(name = "securityManager")public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//管理userRealmsecurityManager.setRealm(userRealm);return securityManager;}//创建realm对象@Beanpublic UserRealm userRealm() {return new UserRealm();}}

shiro实现登陆认证

@Bean
public ShiroFilterFactoryBean getShiroFilterFactorBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();//设置安全管理bean.setSecurityManager(defaultWebSecurityManager);//添加shiro的内置过滤器实现登陆认证/*anno: 不用认证直接访问authc: 认证之后才能访问user: 开启记住我功能才能访问perms: 拥有某个资源的权限才能访问role: 拥有某个角色权限才能访问*/LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();//设置登陆拦截filterMap.put("/user/add", "authc");filterMap.put("/user/update", "authc");bean.setFilterChainDefinitionMap(filterMap);//设置登陆请求bean.setLoginUrl("/toLogin");return bean;
}

shiro实现用户认证

userRealm

//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("执行了认证");String username = "root";String password = "123456";UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;if (!userToken.getUsername().equals(username)) {return null;//返回空,shiro会帮助我们抛出一个不明账户异常(UnknownAccountException),我们就可以在调用类中捕获来处理这种情况}//shiro不需要我们手动认证密码。它的实现类会自动帮我们验证密码。我们只需要新建一个实现类,将用户密码传递进去即可return new SimpleAuthenticationInfo("",password,"");
}

控制器

@RequestMapping("/login")
public String login(String username, String password, Model model) {//获取当前用户Subject subject = SecurityUtils.getSubject();//将前端提交的表单数据封装成令牌UsernamePasswordToken token = new UsernamePasswordToken(username, password);try {//使用令牌登陆subject.login(token);//执行登陆方法,shrio帮助我们认证用户//shiro会在这里帮助我们调用realm的认证方法。从而使用到userrealm类return "index";} catch (UnknownAccountException e) {//异常捕获,我们用来处理一些错误情况,给前端返回一些错误信息//用户不存在model.addAttribute("msg", "用户名错误");return "login";} catch (IncorrectCredentialsException e) {//密码错误model.addAttribute("msg", "密码错误");return "login";}
}

shiro整合mybatis

导入依赖

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.14</version>
</dependency>
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version>
</dependency>

配置数据源

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://localhost:3306/myabtis?useUnicode=true&&characterEncoding=utf-8&&useSSL=falseusername: rootpassword: 123456#durid数据源专有配置initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: select 1 from dualtestWileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: true#配置监控统计的拦截的filter,stat:监控统计、log4j:日志记录、wall:防御sql注入#如果运行时报错 类找不到异常:log4j#就导入log4j依赖filters: stat,wall,log4jmaxPoolPreparedStatementPerConnectionSize: 20userGlobalDataSourceStat: trueconnectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500#mybatis配置
mybatis:type-aliases-package: com.zhong.pojomapper-locations: classpath:mybatis/mapper/*.xml

userRealm

package com.zhong.config;import com.zhong.pojo.User;
import com.zhong.service.UserServiceImpl;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;import javax.annotation.Resource;public class UserRealm extends AuthorizingRealm {//整合mybatis,连接到数据库@ResourceUserServiceImpl userService;//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了授权");return null;}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("执行了认证");UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;//连接数据库User user = userService.queryUserByName(userToken.getUsername());if (user == null) {return null;//返回空,shiro会帮助我们抛出一个不明账户异常(UnknownAccountException),我们就可以在调用类中捕获来处理这种情况}//shiro不需要我们手动认证密码。它的实现类会自动帮我们验证密码。我们只需要新建一个实现类,将数据库的用户密码丢给它,让他去验证用户输入的密码即可return new SimpleAuthenticationInfo("",user.getPwd(),"");}
}

shrio请求授权实现

我们需要在shiro的配置文件中,配置授权信息

package com.zhong.config;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.LinkedHashMap;@Configuration
public class ShiroConfig {//shiroFilterFactoryBean@Beanpublic ShiroFilterFactoryBean getShiroFilterFactorBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();//设置安全管理bean.setSecurityManager(defaultWebSecurityManager);//添加shiro的内置过滤器实现登陆认证/*anno: 不用认证直接访问authc: 认证之后才能访问user: 开启记住我功能才能访问perms: 拥有某个资源的权限才能访问role: 拥有某个角色权限才能访问*///授权组,用来存放资源被授权给哪些权限LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();//将该路劲下的资源文件授权给拥有user:add权限的用户。其他用户访问会报401filterMap.put("/user/add","perms[user:add]");filterMap.put("/user/update","perms[user:update]");//设置user路径下的所有请求需要先认证才能放行filterMap.put("/user/*", "authc");//将权限组放进shiro责任链实例对象beanbean.setFilterChainDefinitionMap(filterMap);//设置登陆请求bean.setLoginUrl("/toLogin");//设置未授权请求bean.setUnauthorizedUrl("/noauth");return bean;}}

控制器配置未授权请求跳转

@RequestMapping("/noauth")
@ResponseBody
public String unauthorized() {return "未经授权,无法访问";
}

配置认证

package com.zhong.config;import com.zhong.pojo.User;
import com.zhong.service.UserServiceImpl;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;import javax.annotation.Resource;public class UserRealm extends AuthorizingRealm {//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了授权");//设置授权SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();//设置授权名称
//        info.addStringPermission("user:add");//拿到当前登陆的对象Subject subject = SecurityUtils.getSubject();User currentUser = (User) subject.getPrincipal();//取出认证方法中存入的用户//设置当前用户权限info.addStringPermission(currentUser.getPerms());return info;}}

用户注销

filterMap.put("/logout", "logout");
//shiro注销实现在配置文件的内置过滤器,控制器只需要做跳转即可
@RequestMapping("/logout")
public String logout() {return "redirect:/toLogin";
}

shrio整合thymeleaf

动态菜单

导入依赖

<dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.1.0</version>
</dependency>

配置shrio配置文件,创建一个整合实例对象托管给spring

//整合shrioDialect:用来整合thymeleaf
@Bean
public ShiroDialect getShiroDialect() {return new ShiroDialect();
}

命名空间导入

xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"

通过判断认证的身份来动态显示子元素

<!--shiro:guest=""验证是是否为未登录用户,是就显示子元素-->
<a th:href="@{/toLogin}" shiro:guest="">登陆</a><!--hasAnyPermissions拥有指定的任何一个权限即可显示子元素-->
<div shiro:hasAnyPermissions="'user:add','user:update'"></div><!--指定拥有特定权限的用户才会显示子元素-->
<div shiro:hasPermission="'user:add'"><a th:href="@{/user/add}">add</a> <br>
</div>
<div shiro:hasPermission="'user:uodate'"><a th:href="@{/user/update}">update</a>
</div>

Swagger

  • Restful Api 文档在线自动生成工具,Api文档和Api定义同步更新
  • 直接运行,可以在线测试

在项目中使用swagger需要springfox

  • swagger2
  • swaggerUI

在springboot中集成swagger

在springboot2.7以上的版本可能会和swagger存在不兼容的问题,原因是springboot改变了swagger需要的一个源码的路径,为了兼容swagger3我们需要做一些配置。

  1. 新建一个项目,依赖中含有web启动器

  2. 导入swagger依赖

    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
    <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>3.0.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
    <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>3.0.0</version>
    </dependency>
    <dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version>
    </dependency>
    
  3. 配置application.yml

    spring:mvc:pathmatch:matching-strategy: ant_path_matcher
    
  4. 在启动类上加上注解@EnableOpenApi

    package com.zhong.swagger;import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import springfox.documentation.oas.annotations.EnableOpenApi;@SpringBootApplication
    @EnableOpenApi
    public class SwaggerDemoApplication {public static void main(String[] args) {SpringApplication.run(SwaggerDemoApplication.class, args);}}
    
  5. 创建swagger的配置类config/SwaggerConfig.java

    package com.zhong.swagger.config;import org.springframework.context.annotation.Configuration;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;@Configuration //让springboot帮助我们把swagger加载到配置中
    @EnableSwagger2 //开启swagger配置
    public class SwaggerConfig {
    }
    
  6. 创建测试的类

    package com.zhong.swagger.controller;import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;@RestController
    public class HelloController {@RequestMapping("/hello")public String hello() {return "hello";}
    }
    
  7. 运行项目并访问http://localhost:8080/swagger-ui/index.html,成功会显示如下内容

    在这里插入图片描述

swagger配置

public class SwaggerConfig {//配置swagger的docket的bean实例@Beanpublic Docket docket() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());}//配置swagger文档基本信息private ApiInfo apiInfo() {//作者信息Contact contact = new Contact("钟", "", "2132121@qq.com");return new ApiInfo("swagger文档","这里填写文档信息","v1.0","urn:tos",contact,"Apache 2.0","http://www.apache.org/licenses/LICENSE-2.0",new ArrayList());}
}

swagger配置扫描接口

//配置swagger的docket的bean实例
@Bean
public Docket docket() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())//配置接口扫描.select()//RequestHandlerSelectors配置接口扫描的方式//.basePackage()基于包名扫描//.any()扫描全部//.none()全部不扫描//.withClassAnnotation()扫描类上的注解,需要一个注解的反射对象//.withMethodAnnotation()扫描方法上的注解,需要一个注解的反射对象.apis(RequestHandlerSelectors.withMethodAnnotation())//过滤器,可以让swagger不扫描哪些路径//PathSelectors.ant()参数传递com包下的路径.paths(PathSelectors.ant())//建造者模式.build();
}

配置是否启动swagge

//配置swagger的docket的bean实例
@Bean
public Docket docket() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())//配置swagger是否开启.enable(false).select().apis(RequestHandlerSelectors.basePackage("com.zhong.swagger.controller")).build();
}

根据配置文件配置的环境决定是否开启swagger

#激活环境
spring:profiles:active: promvc:pathmatch:matching-strategy: ant_path_matcher
#多文档模式,使用 --- 分隔开
---
# 在2.4之后的版本中推荐使用on-profile命名环境
spring:config:activate:on-profile: dev
---
spring:config:activate:on-profile: pro
public class SwaggerConfig {@Beanpublic Docket docket(Environment environment) {//配置需要开启swagger的环境Profiles profiles = Profiles.of("dev","test");//接收一个环境名字判断是否处于该环境中boolean flag = environment.acceptsProfiles(profiles);return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).enable(flag)}}

配置API文档分组

.groupName("zhong")

配置多个文档分组也就是创建多个Docket类并且注册到bean,并且使用不同的groupname

swagger中的model

只要被swagger扫描的接口中存在有接口的返回值是一个实体类,那么这个实体类就会被swagger扫描并且配置到swagger文档中,实体类中必须存在属性的get方法,该属性才会被swagger扫描到文档中。可以用过注解@ApiModel和@ApiModelProperty来给文档中的model层加上一些注释

@ApiModel("用户实体类")
public class User {@ApiModelProperty("用户名")private String username;@ApiModelProperty("密码")private String password;
}

@ApiOperation()给接口加上swagger注释,@ApiParam()给接口中的参数加上注释

任务

异步任务

在需要开启多线程处理的业务中加上注解@Async表示这是一个异步任务

@Service
public class AsyncService {//告诉spring这是一个异步任务@Asyncpublic void hello() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("数据处理完成");}
}

在启动类上需要加上@EnableAsync,之后在控制器中正常调用就好,spring会帮我们自动开启多线程处理

@EnableAsync//开启异步注解扫描
@SpringBootApplication
public class Springboot09TestApplication {public static void main(String[] args) {SpringApplication.run(Springboot09TestApplication.class, args);}
}

定时任务

在启动类上加上注解开启定时任务@EnableScheduling

//cron是指在linux上是时间服务器
//这里使用cron表达式来定时,秒 分 时 日 月 星期
//定时任务本身就是一个异步任务,到了指定的时间自动执行
@Scheduled(cron = "40 46 19 * * ?")//在周一到周天中,任何时间内的第0秒执行这个程序
public void hello() {System.out.println("hello被执行");
}

邮件发送

springboot中配置邮件任务需要一个邮件启动器的依赖,不过根据springboot的版本不同可能会存在mail启动器没有这个版本下的依赖,这个时候需要手动设置mail启动器的版本

springboot版本为2.7.9

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId><version>2.7.8</version>
</dependency>

简单邮件发送

配置

spring:mail:username: 99268317@qq.compassword: oqfmuurtzrhost: smtp.qq.com# qq邮箱需要配置SSL加密properties.mail.smtl.ssl.enable=: true

邮件发送

@Resource
private JavaMailSender javaMailSender;@Test
void contextLoads() {// 简单邮件发送SimpleMailMessage simpleMailMessage = new SimpleMailMessage();//封装一个邮件对象simpleMailMessage.setSubject("springboot邮件发送测试");//设置邮件标题simpleMailMessage.setText("刘先生,收到请回复收到请回复,over");//设置邮件正文simpleMailMessage.setTo("1426717@qq.com");//设置收件人simpleMailMessage.setFrom("992616@qq.com");//设置发件人javaMailSender.send(simpleMailMessage);//调用发送}

复杂邮件发送

@Test
void contextLoads2() throws MessagingException {//复杂邮件发送MimeMessage mimeMessage = javaMailSender.createMimeMessage();//创建一个复杂邮件对象//对复杂邮件对象进行封装MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);//必须设置一个邮件标题和正文mimeMessageHelper.setSubject("springboot附件邮件测试");mimeMessageHelper.setText("<p style='color:red'>带有附件的复杂邮件类型</p>", true);//附件mimeMessageHelper.addAttachment("1.jpg", new File("D:\\sourceCode\\JAVA\\springboot-09-test\\target\\classes\\static\\1.jpg"));//封装源和目标mimeMessageHelper.setTo("1420917@qq.com");mimeMessageHelper.setFrom("992616@qq.com");javaMailSender.send(mimeMessage);//调用发送
}

相关文章:

springboot笔记

微服务架构 微服务是一种架构风格&#xff0c;开发构建应用的时候把应用的业务构建成一个个的小服务&#xff08;这就类似于把我们的应用程序构建成了一个个小小的盒子&#xff0c;它们在一个大的容器中运行&#xff0c;这种一个个的小盒子我们把它叫做服务&#xff09;&#…...

【多线程与高并发】- 浅谈volatile

浅谈volatile简介JMM概述volatile的特性1、可见性举个例子总结2、无法保证原子性举个例子分析使用volatile对原子性测试使用锁的机制总结3、禁止指令重排什么是指令重排序重排序怎么提高执行速度重排序的问题所在volatile禁止指令重排序内存屏障(Memory Barrier)作用volatile内…...

avro格式详解

【Avro介绍】Apache Avro是hadoop中的一个子项目&#xff0c;也是一个数据序列化系统&#xff0c;其数据最终以二进制格式&#xff0c;采用行式存储的方式进行存储。Avro提供了&#xff1a;丰富的数据结构可压缩、快速的二进制数据格式一个用来存储持久化数据的容器文件远程过程…...

【涨薪技术】0到1学会性能测试 —— LR录制回放事务检查点

前言 上一次推文我们分享了性能测试分类和应用领域&#xff0c;今天带大家学习性能测试工作原理、事务、检查点&#xff01;后续文章都会系统分享干货&#xff0c;带大家从0到1学会性能测试&#xff0c;另外还有教程等同步资料&#xff0c;文末免费获取~ 01、LR工作原理 通常…...

卡尔曼滤波原理及代码实战

目录简介1.原理介绍场景假设(1).下一时刻的状态(2).增加系统的内部控制(3).考虑运动系统外部的影响(4).后验估计&#xff1a;预测结果与观测结果的融合卡尔曼增益K2.卡尔曼滤波计算过程(1).预测阶段&#xff08;先验估计阶段&#xff09;(2).更新阶段&#xff08;后验估计阶段&…...

Jmeter使用教程

目录一&#xff0c;简介二&#xff0c;Jmeter安装1&#xff0c;下载2&#xff0c;安装三&#xff0c;创建测试1&#xff0c;创建线程组2&#xff0c;创建HTTP请求默认值3&#xff0c;创建HTTP请求4&#xff0c;添加HTTP请求头5&#xff0c;添加断言6&#xff0c;添加查看结果树…...

论文笔记|固定效应的解释和使用

DeHaan E. Using and interpreting fixed effects models[J]. Available at SSRN 3699777, 2021. 虽然固定效应在金融经济学研究中无处不在&#xff0c;但许多研究人员对作用的了解有限。这篇论文解释了固定效应如何消除遗漏变量偏差并影响标准误差&#xff0c;并讨论了使用固…...

数据集市与数据仓库的区别

数据仓库是企业级的&#xff0c;能为整个企业各个部门的运作提供决策支持&#xff1b;而数据集市则是部门级的&#xff0c;一般只能为某个局部范围内的管理人员服务&#xff0c;因此也称之为部门级数据仓库。 1、两种数据集市结构 数据集市按数据的来源分为以下两种 &#x…...

Golang学习Day3

&#x1f60b; 大家好&#xff0c;我是YAy_17&#xff0c;是一枚爱好网安的小白。 本人水平有限&#xff0c;欢迎各位师傅指点&#xff0c;欢迎关注 &#x1f601;&#xff0c;一起学习 &#x1f497; &#xff0c;一起进步 ⭐ 。 ⭐ 此后如竟没有炬火&#xff0c;我便是唯一的…...

Python并发编程-事件驱动模型

一、事件驱动模型介绍 1、传统的编程模式 例如&#xff1a;线性模式大致流程 开始--->代码块A--->代码块B--->代码块C--->代码块D--->......---&…...

构建系统发育树简述

1. 要点 系统发育树代表了关于一组生物之间的进化关系的假设。可以使用物种或其他群体的形态学&#xff08;体型&#xff09;、生化、行为或分子特征来构建系统发育树。在构建树时&#xff0c;我们根据共享的派生特征&#xff08;不同于该组祖先的特征&#xff09;将物种组织成…...

这款 Python 调试神器推荐收藏

大家好&#xff0c;对于每个程序开发者来说&#xff0c;调试几乎是必备技能。 代码写到一半卡住了&#xff0c;不知道这个函数执行完的返回结果是怎样的&#xff1f;调试一下看看 代码运行到一半报错了&#xff0c;什么情况&#xff1f;怎么跟预期的不一样&#xff1f;调试一…...

金三银四吃透这份微服务笔记,面试保准涨10K+

很多人对于微服务技术也都有着一些疑虑&#xff0c;比如&#xff1a; 微服务这技术虽然面试的时候总有人提&#xff0c;但作为一个开发&#xff0c;是不是和我关系不大&#xff1f;那不都是架构师的事吗&#xff1f;微服务不都是大厂在玩吗&#xff1f;我们这个业务体量用得着…...

构建matter over Thread的演示系统-efr32

文章目录1. 简介2. 构建测试系统2.1设置 Matter Hub(Raspberry Pi)2.2 烧录Open Thread RCP固件2.3 烧录待测试的matter设备3. 配网和测试&#xff1a;3.1 使用mattertool建立Thread网络3.2 使用mattertool配置设备入网3.3 使用mattertool控制matter设备3.4 查看节点的Node ID等…...

【一天一门编程语言】Matlab 语言程序设计极简教程

Matlab 语言程序设计极简教程 用 markdown 格式输出答案。 不少于3000字。细分到2级目录。 目录 Matlab 语言程序设计极简教程 简介Matlab 工作空间Matlab 基本数据类型Matlab 语句和表达式Matlab 函数和程序Matlab 图形界面程序设计Matlab 应用实例 简介 Matlab是一种编…...

看似平平无奇的00后,居然一跃上岸字节,表示真的卷不过......

又到了一年一度的求职旺季金&#xff01;三&#xff01;银&#xff01;四&#xff01;在找工作的时候都必须要经历面试这个环节。在这里我想分享一下自己上岸字节的面试经验&#xff0c;过程还挺曲折的&#xff0c;但是还好成功上岸了。大家可以参考一下&#xff01; 0821测评 …...

BZOJ2142 礼物

题目描述 一年一度的圣诞节快要来到了。每年的圣诞节小E都会收到许多礼物&#xff0c;当然他也会送出许多礼物。不同的人物在小E 心目中的重要性不同&#xff0c;在小E心中分量越重的人&#xff0c;收到的礼物会越多。小E从商店中购买了n件礼物&#xff0c;打算送给m个人 &…...

MySQL高级第一讲

目录 一、MySQL高级01 1.1 索引 1.1.1 索引概述 1.1.2 索引特点 1.1.3 索引结构 1.1.4 BTREE结构(B树) 1.1.5 BTREE结构(B树) 1.1.6 索引分类 1.1.7 索引语法 1.1.8 索引设计原则 1.2 视图 1.2.1 视图概述 1.2.2 创建或修改视图 1.3 存储过程和函数 1.3.1 存储过…...

前端面试常用内容——基础积累

1.清除浮动的方式有哪些&#xff1f; 高度塌陷&#xff1a;当所有的子元素浮动的时候&#xff0c;且父元素没有设置高度&#xff0c;这时候父元素就会产生高度塌陷。 清除浮动的方式&#xff1a; 1.1 给父元素单独定义高度 优点&#xff1a; 快速简单&#xff0c;代码少 缺…...

跟着《代码随想录》刷题(三)——哈希表

3.1 哈希表理论基础 哈希表理论基础 3.2 有效的字母异位词 242.有效的字母异位词 C bool isAnagram(char * s, char * t){int array[26] {0};int i 0;while (s[i]) {// 并不需要记住字符的ASCII码&#xff0c;只需要求出一个相对数值就可以了array[s[i] - a];i;}i 0;whi…...

HTML - 扫盲

文章目录1. 前言2. HTML2.1 下载 vscode3 HTML 常见标签3.1 注释标签3.2 标题标签3.3 段落标签3.4 换行标签3.5 格式化标签1. 加粗2. 倾斜3. 下划线3.6 图片标签3.7 超链接标签3.8 表格标签3.9 列表标签4. 表单标签4.1 from 标签4.2 input 标签4.3 select 标签4.4 textarea标签…...

【系统分析师之路】2022上案例分析历年真题

【系统分析师之路】2022上案例分析历年真题 【系统分析师之路】2022上案例分析历年真题【系统分析师之路】2022上案例分析历年真题2022上案例分析历年真题第一题&#xff08;25分&#xff09;2022上案例分析历年真题第二题&#xff08;25分&#xff09;2022上案例分析历年真题第…...

Python编程规范

Python编程规范 当今Python编程社区有许多关于编程规范的约定和惯例。以下是一些常见的Python编程规范&#xff1a; 1.使用有意义的命名 使用有意义的命名可以使代码更加清晰、易读、易维护。变量、函数、类和模块的命名应该能够明确传达其用途&#xff0c;而不是使用无意义…...

【Java】Spring Boot项目的创建和使用

文章目录SpringBoot的创建和使用1. 什么是Spring Boot&#xff1f;为什么要学Spring Boot&#xff1f;2. Spring Boot项目的优点3. Spring Boot 项目的创建3.1 使用idea创建3.2 接下来创建Spring Boot项目4. 项目目录介绍和运行4.1 运行项目4.2 输出内容5. 总结SpringBoot的创建…...

Malware Dev 00 - Rust vs C++ 初探

写在最前 如果你是信息安全爱好者&#xff0c;如果你想考一些证书来提升自己的能力&#xff0c;那么欢迎大家来我的 Discord 频道 Northern Bay。邀请链接在这里&#xff1a; https://discord.gg/9XvvuFq9Wb我会提供备考过程中尽可能多的帮助&#xff0c;并分享学习和实践过程…...

JavaScript HTML DOM 事件

文章目录JavaScript HTML DOM 事件对事件做出反应HTML 事件属性使用 HTML DOM 来分配事件onload 和 onunload 事件onchange 事件onmouseover 和 onmouseout 事件onmousedown、onmouseup 以及 onclick 事件JavaScript HTML DOM 事件 HTML DOM 使 JavaScript 有能力对 HTML 事件做…...

推荐算法——NCF知识总结代码实现

NCF知识总结代码实现1. NeuralCF 模型的结构1.1 回顾CF和MF1.2 NCF 模型结构1.3 NeuralCF 模型的扩展---双塔模型2. NCF代码实现2.1 tensorflow2.2 pytorchNeuralCF&#xff1a;如何用深度学习改造协同过滤&#xff1f; 随着技术的发展&#xff0c;协同过滤相比深度学习模型的…...

redis(4)String字符串

前言 Redis中有5大数据类型&#xff0c;分别是字符串String、列表List、集合Set、哈希Hash、有序集合Zset&#xff0c;本篇介绍Redis的字符串String Redis字符串 String是Redis最基本的类型&#xff0c;你可以理解成与Memcached一模一样的类型&#xff0c;一个key对应一个value…...

session一致性问题

在http访问请求中&#xff0c;web服务器会自动为同一个浏览器的访问用户自动创建唯一的session&#xff0c;提供数据存储功能。最常见的&#xff0c;会把用户的登录信息、用户信息存储在session中&#xff0c;以保持登录状态。只要用户不重启浏览器&#xff0c;每次http短连接请…...

上岸16K,薪资翻倍,在华为外包做测试是一种什么样的体验····

现在回过头看当初的决定&#xff0c;还是正确的&#xff0c;自己转行成功&#xff0c;现在进入了华为外包测试岗&#xff0c;脱离了工厂生活&#xff0c;薪资也翻了一倍不止。 我17年毕业于一个普通二本学校&#xff0c;电子信息工程学院&#xff0c;是一个很不出名的小本科。…...