順平網站建設域名比價網
微服務架構
微服務是一種架構風格,開發(fā)構建應用的時候把應用的業(yè)務構建成一個個的小服務(這就類似于把我們的應用程序構建成了一個個小小的盒子,它們在一個大的容器中運行,這種一個個的小盒子我們把它叫做服務),通過服務的組合來完成具體的業(yè)務。服務之間相互獨立,互不影響
第一個springboot程序
環(huán)境:
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的父依賴規(guī)劃好了所有我們可能會用到的jar包的版本。選擇了springboot的版本也就是選擇了之后所有用到jar包的版本。springboot核心依賴都在父工程中
springboot啟動器
所有帶有starter的依賴都是springboot的啟動器。啟動器其實就是springboot把我們實際會用到的的項目的一些場景給抽象出來了。比如web啟動器,就是給web環(huán)境準備的,它會把web需要用到的jar打包進入一個依賴(這里依賴就是啟動器),我們只需要導入這個springboot打包好的依賴,就可以直接進行相關場景的開發(fā)。
<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.數(shù)組
pets:- dog- cat- pig# 5.數(shù)組的行內寫法
pets2: [cat, dog, pig]
yaml給實體類賦值@ConfigurationProperties
首先在application.yaml中準備好數(shù)據(jù)
person:name: zhongage: 3happy: falsebirth: 2000/1/1maps: {k1: va, k2: v2}list:- code- music- girldog:name: 旺財age: 3
在實體類中加上注解@ConfigurationProperties(prefix = “person”),括號內的是指定需要給實體類綁定yaml文件中的哪個數(shù)據(jù)。加上注解之后會爆紅提示我們配置,但是這個配置可有可無,它并不影響程序的正常運行,如需解決爆紅,在pom.xml文件中加入依賴
JSR303校驗
@Validated在類上標注,意思是開啟數(shù)據(jù)校驗。然后通過在屬性上添加注解來實現(xiàn)校驗。
多環(huán)境配置和配置文件的位置
配置文件可以在四個位置被檢測到
優(yōu)先級:項目/config > 項目 > resource/config > resource
首先第二套環(huán)境配置文件名為application-dev.yaml
# springboot 的多環(huán)境配置可以選擇激化哪一個環(huán)境
spring:profiles:active: dev
yaml存在多文檔格式
server:port: 8081
spring:profiles:active: test#多文檔模式,使用 --- 分隔開
---server:port: 8082
# 給第二套環(huán)境命名,此方式已經棄用
spring:profiles: test
# 在2.4之后的版本中推薦使用
spring:config:activate:on-profile: dev
自動配置原理理解
AutoConfiguration注解去裝載一個XXXproperties.java文件,這個文件又去通過注解加載了yml文件中我們自定義的一些配置。那么如果我們yml中的配置存在,XXXproperties文件生效。自動裝配去找到這個文件并且把它加載進來實現(xiàn)加載我們的自定義配置。如果不存在自定義配置,那么XXXproperties就會失效,AutoConfiguration只會加載它自己已經定義好的一些默認值配置
總結
springboot啟動會加載大量的配置類
看我們需要的功能在springboot默認寫好的配置類中是否存在
配置類中存在哪些組件(只要我們要用的組件存在,我們就不需要手動配置)
給容器中自動配置類添加組件的時候,會從xxproperties類中獲取某些屬性,我們需要在配置文件中指定這些屬性的值就好
springboot的web開發(fā)
導入靜態(tài)資源
可以使用maven的方式導入前端的一些包(以webjar的方式)
<dependency><groupId>org.webjars</groupId><artifactId>jquery</artifactId><version>3.4.1</version>
</dependency>
resource下的這些目錄的內容都可以被直接訪問
優(yōu)先級: resource > static > public
制作首頁
springboot有自己的一個首頁的配置類,它指定了一個在resource目錄下的index首頁文件會被進入項目于時加載,如果我們沒有指定他就會加載配置好的一個默認的首頁。
我們在resource目錄的static目錄下新建index.html文件。啟動項目即可直接加載該文件
在templates目錄下的所有資源,只能夠通過controller跳轉或訪問。類似于jsp開發(fā)中是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底層實現(xiàn)*/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的路徑寫法使用@{/} 這里/代表項目根路徑,下面的靜態(tài)資源目錄不用寫,直接使用它們內部的目錄,靜態(tài)資源都使用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屬性來指定返回的語言。在@{}路徑中使用()來傳遞參數(shù)
<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) {//獲取請求中的語言參數(shù)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();
}
項目實戰(zhàn)
登陸功能實現(xiàn)
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) {//登陸業(yè)務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();}
}
攔截器
新建類實現(xiàn)攔截器接口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) {//登陸業(yè)務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>
需要使用~{抽取出插件的頁面名字::插件名字},同時這種寫法也可使用()傳遞參數(shù),()傳遞參數(shù)就相當于以前的jsp?傳遞參數(shù)
在動態(tài)設置導航欄激活狀態(tài)的時候,可以使用表達式加上三元運算符來判斷激活狀態(tài)
<!-- 接收參數(shù)并且判斷-->
<a th:class="${active=='main.html'?'nav-link active':'nav-link'}" th:href="@{/main}"></a>
<!--傳遞參數(shù)-->
<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>
配置數(shù)據(jù)源,這里可能需要設置時區(qū),但是在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 {//默認數(shù)據(jù)源System.out.println(dataSource.getClass());Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();
}
這里介紹一種springboot寫好的bean模板叫做 XXXtemplate,例如springboot寫好的jdbc的bean就叫做jdbc template,使用它可以直接連接數(shù)據(jù)庫。并且這個被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數(shù)據(jù)源
導入依賴
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.14</version>
</dependency>
通過type指定springboot的數(shù)據(jù)源
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數(shù)據(jù)源專有配置initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: select 1 from dualtestWileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: true#配置監(jiān)控統(tǒng)計的攔截的filter,stat:監(jiān)控統(tǒng)計、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();}//后臺監(jiān)控//ServletRegistrationBean意思是幫助我們把一些druid的插件注冊成bean@Bean //需要注冊為beanpublic ServletRegistrationBean statViewServlet() {//new一個bean的注冊,參數(shù)傳遞一些插件,同時指定路徑,進入該路徑的時候會進入該插件的圖形界面ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");//監(jiān)控后臺需要登陸HashMap<String, String> initParameters = new HashMap<>();//增加配置initParameters.put("loginUsername","admin");//loginUsername固定寫法initParameters.put("loginPassword","123456");//固定寫法//允許訪問initParameters.put("allow", "");//如果該屬性為空,則代表所有人可以訪問//禁止訪問
// initParameters.put("kuangsheng", "這里寫ip地址");//只要這樣配置之后,這個ip地址就會被禁止訪問監(jiān)控后臺bean.setInitParameters(initParameters);//設置初始化參數(shù)return bean;}//注冊filter@Beanpublic FilterRegistrationBean webStatFilter() {FilterRegistrationBean bean = new FilterRegistrationBean();bean.setFilter(new WebStatFilter());HashMap<String, String> initParameters = new HashMap<>();//對請求這些資源或者路勁的請求不統(tǒng)計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>
配置數(shù)據(jù)源
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# 配置接口實現(xiàn)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的實現(xiàn)
<?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 {//實現(xiàn)首頁所有人可以訪問,功能頁只有對應的人可以訪問//認證請求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 {//從內存拿到數(shù)據(jù)來進行驗證//這里需要配置一個密碼加密方式,否則會跑不起來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);}}
注銷功能實現(xiàn)
//開啟注銷功能,并清除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">
動態(tài)顯示內容
<!--登錄注銷-->
<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>
<!--動態(tài)菜單-->
<div class="column" sec:authorize="hasRole('vip1')">
跨域配置
//關閉默認開啟的網站防御攻擊,以此支持跨域請求
http.csrf().disable();
記住我和首頁定制
開啟記住我功能會保存用戶的登陸狀態(tài),通過向用戶電腦存放一個cookie來實現(xiàn)保存登陸狀態(tài),cookie有效期為兩周
http.rememberMe();
定制登錄頁
//定制登錄頁,并且指定處理登陸請求的控制器。這里表單提交必須要是post
.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");
定制登錄頁的前提是,前端傳遞的參數(shù)必須是username和password,如果它們的name是其他的名稱,框架識別不到就會登陸失敗。當然,這種參數(shù)名字也可以定制,通過以下兩種方法為參數(shù)指定一個新的接收前端參數(shù)的名字。
.formLogin().loginPage("/toLogin").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login");
同樣的,記住我功能也可以定制參數(shù)
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("執(zhí)行了授權");return null;}//認證@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("執(zhí)行了認證");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;}//創(chuàng)建realm對象@Beanpublic UserRealm userRealm() {return new UserRealm();}}
shiro實現(xiàn)登陸認證
@Bean
public ShiroFilterFactoryBean getShiroFilterFactorBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();//設置安全管理bean.setSecurityManager(defaultWebSecurityManager);//添加shiro的內置過濾器實現(xiàn)登陸認證/*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實現(xiàn)用戶認證
userRealm
//認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("執(zhí)行了認證");String username = "root";String password = "123456";UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;if (!userToken.getUsername().equals(username)) {return null;//返回空,shiro會幫助我們拋出一個不明賬戶異常(UnknownAccountException),我們就可以在調用類中捕獲來處理這種情況}//shiro不需要我們手動認證密碼。它的實現(xiàn)類會自動幫我們驗證密碼。我們只需要新建一個實現(xiàn)類,將用戶密碼傳遞進去即可return new SimpleAuthenticationInfo("",password,"");
}
控制器
@RequestMapping("/login")
public String login(String username, String password, Model model) {//獲取當前用戶Subject subject = SecurityUtils.getSubject();//將前端提交的表單數(shù)據(jù)封裝成令牌UsernamePasswordToken token = new UsernamePasswordToken(username, password);try {//使用令牌登陸subject.login(token);//執(zhí)行登陸方法,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>
配置數(shù)據(jù)源
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數(shù)據(jù)源專有配置initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: select 1 from dualtestWileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: true#配置監(jiān)控統(tǒng)計的攔截的filter,stat:監(jiān)控統(tǒng)計、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,連接到數(shù)據(jù)庫@ResourceUserServiceImpl userService;//授權@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("執(zhí)行了授權");return null;}//認證@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("執(zhí)行了認證");UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;//連接數(shù)據(jù)庫User user = userService.queryUserByName(userToken.getUsername());if (user == null) {return null;//返回空,shiro會幫助我們拋出一個不明賬戶異常(UnknownAccountException),我們就可以在調用類中捕獲來處理這種情況}//shiro不需要我們手動認證密碼。它的實現(xiàn)類會自動幫我們驗證密碼。我們只需要新建一個實現(xiàn)類,將數(shù)據(jù)庫的用戶密碼丟給它,讓他去驗證用戶輸入的密碼即可return new SimpleAuthenticationInfo("",user.getPwd(),"");}
}
shrio請求授權實現(xiàn)
我們需要在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的內置過濾器實現(xiàn)登陸認證/*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("執(zhí)行了授權");//設置授權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注銷實現(xiàn)在配置文件的內置過濾器,控制器只需要做跳轉即可
@RequestMapping("/logout")
public String logout() {return "redirect:/toLogin";
}
shrio整合thymeleaf
動態(tài)菜單
導入依賴
<dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.1.0</version>
</dependency>
配置shrio配置文件,創(chuàng)建一個整合實例對象托管給spring
//整合shrioDialect:用來整合thymeleaf
@Bean
public ShiroDialect getShiroDialect() {return new ShiroDialect();
}
命名空間導入
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
通過判斷認證的身份來動態(tài)顯示子元素
<!--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我們需要做一些配置。
-
新建一個項目,依賴中含有web啟動器
-
導入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>
-
配置application.yml
spring:mvc:pathmatch:matching-strategy: ant_path_matcher
-
在啟動類上加上注解@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);}}
-
創(chuàng)建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 { }
-
創(chuàng)建測試的類
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";} }
-
運行項目并訪問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()參數(shù)傳遞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();
}
根據(jù)配置文件配置的環(huán)境決定是否開啟swagger
#激活環(huán)境
spring:profiles:active: promvc:pathmatch:matching-strategy: ant_path_matcher
#多文檔模式,使用 --- 分隔開
---
# 在2.4之后的版本中推薦使用on-profile命名環(huán)境
spring:config:activate:on-profile: dev
---
spring:config:activate:on-profile: pro
public class SwaggerConfig {@Beanpublic Docket docket(Environment environment) {//配置需要開啟swagger的環(huán)境Profiles profiles = Profiles.of("dev","test");//接收一個環(huán)境名字判斷是否處于該環(huán)境中boolean flag = environment.acceptsProfiles(profiles);return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).enable(flag)}}
配置API文檔分組
.groupName("zhong")
配置多個文檔分組也就是創(chuàng)建多個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()給接口中的參數(shù)加上注釋
任務
異步任務
在需要開啟多線程處理的業(yè)務中加上注解@Async表示這是一個異步任務
@Service
public class AsyncService {//告訴spring這是一個異步任務@Asyncpublic void hello() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("數(shù)據(jù)處理完成");}
}
在啟動類上需要加上@EnableAsync,之后在控制器中正常調用就好,spring會幫我們自動開啟多線程處理
@EnableAsync//開啟異步注解掃描
@SpringBootApplication
public class Springboot09TestApplication {public static void main(String[] args) {SpringApplication.run(Springboot09TestApplication.class, args);}
}
定時任務
在啟動類上加上注解開啟定時任務@EnableScheduling
//cron是指在linux上是時間服務器
//這里使用cron表達式來定時,秒 分 時 日 月 星期
//定時任務本身就是一個異步任務,到了指定的時間自動執(zhí)行
@Scheduled(cron = "40 46 19 * * ?")//在周一到周天中,任何時間內的第0秒執(zhí)行這個程序
public void hello() {System.out.println("hello被執(zhí)行");
}
郵件發(fā)送
springboot中配置郵件任務需要一個郵件啟動器的依賴,不過根據(jù)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>
簡單郵件發(fā)送
配置
spring:mail:username: 99268317@qq.compassword: oqfmuurtzrhost: smtp.qq.com# qq郵箱需要配置SSL加密properties.mail.smtl.ssl.enable=: true
郵件發(fā)送
@Resource
private JavaMailSender javaMailSender;@Test
void contextLoads() {// 簡單郵件發(fā)送SimpleMailMessage simpleMailMessage = new SimpleMailMessage();//封裝一個郵件對象simpleMailMessage.setSubject("springboot郵件發(fā)送測試");//設置郵件標題simpleMailMessage.setText("劉先生,收到請回復收到請回復,over");//設置郵件正文simpleMailMessage.setTo("1426717@qq.com");//設置收件人simpleMailMessage.setFrom("992616@qq.com");//設置發(fā)件人javaMailSender.send(simpleMailMessage);//調用發(fā)送}
復雜郵件發(fā)送
@Test
void contextLoads2() throws MessagingException {//復雜郵件發(fā)送MimeMessage mimeMessage = javaMailSender.createMimeMessage();//創(chuàng)建一個復雜郵件對象//對復雜郵件對象進行封裝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);//調用發(fā)送
}