#springboot :
1:是什么
2:配置如何编写
3:自动装配原理:重要!!!
4:集成web开发:业务的核心
5:集成数据库Druid
6:分布式开发:Dubbo(RPC)+zookeeper
7:swagger:接口文档
8:任务调度
9:SpringSecurity ; shiro (做安全的,如登录验证。横向插入)
简化Spring的
springboot就是一个Javaweb的开发框架,和SpringMVC类似,
对比其他Javaweb框架的好处,简化开发
,**约定大于配置**,能迅速开发web应用,几行代码开发一个http接口
springboot默认配置了很多框架的使用方法,整合了所有的框架。
优点:
为所有Spring开发者更快的入门
开箱即用,提供各种默认配置来简化项目配置
内嵌式容器简化web项目
没有冗余代码生成和xml配置的要求
#微服务:
是一种架构模式或架构风格 (如MVC也是架构风格,分成三种架构),
1:模块化:它提倡将单一的应用程序划分成一组小的服务;每个服务运行在其独立的自己的进程内,服务之间互相协调、互相配置,为用户提供最终价值。
2:通过http、RPC框架等方式进行互通:服务之间采用轻量级的通信机制互相沟通,每个服务都围绕具体业务进行构建,并且能被独立部署到生产环境中。
3:分布式:另外,应尽量避免统一的、集中式的服务管理机制,
对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建,
可以有一个非常轻量级的集中式管理来协调这些服务,
可以使用不同的语言来编写服务,也可以使用不同的数据存储。
原始架构all in one方式:把所有的功能单元放在一个应用里,然后把整个应用部署到服务器上,
如果负载能力不行,将整个应用进行水平复制,进行拓展,然后再负载均衡。
缺点:修改一点就需要停掉整个服务,大型应用不可能把所有内容都放在一个应用里,
维护,分工合作问题
微服务架构:把每个功能元素独立出来,自由动态组合,需要的功能元素才拿来组合。所以微服务架构是对功能元素进行复制,而没有对整个应用进行复制。
好处:
节省了调用资源
每个功能元素的服务都是一个可替换的、可独立升级的软件代码
缺点:
开发人员要处理分布式系统的复杂性
多服务运维难度,随着服务的增加,运维的压力也在增大
系统部署依赖
服务间通信成本
数据一致性
系统集成测试
性能监控
微服务技术栈:
服务开发 ---------------> SpringBoot、Spring、SpringMVC
服务配置与管理 ---------------> Netfix公司的Archaius、阿里的Diamond等
服务注册与发现 ---------------> Eureka、Consul、Zookeeper等
服务调用 ---------------> Rest、PRC、gRPC
服务熔断器 ---------------> Hystrix、Envoy等
负载均衡 ---------------> Ribbon、Nginx等
服务接口调用(客户端调用服务的简化工具) ---------------> Feign等
消息队列 ---------------> Kafka、RabbitMQ、ActiveMQ等
服务配置中心管理 ---------------> SpringCloudConfig、Chef等
服务路由(API网关) ---------------> Zuul等
服务监控 ---------------> Zabbix、Nagios、Metrics、Specatator等
全链路追踪 ---------------> Zipkin、Brave、Dapper等
服务部署 ---------------> Docker、OpenStack、Kubernetes等
数据流操作开发包 ---------------> SpringCloud Stream(封装与Redis、Rabbit、kafka等发送接收消息)
事件消息总线 ---------------> SpringCloud Bus
论文:
原文:http://martinfowler.com/articles/microservices.html
译文:http://www.cnblogs.com/liuning8023/p/4493156.html
**Spring为我们带来了构建大型分布式微服务的全套、全程产品**:
构建一个个功能独立的微服务应用单元,可以使用**springboot**,可以帮我们快速构建一个应用
大型分布式网络服务的调用,这部分由**springcloud**完成,实现分布式
在分布式中间,进行流式数据计算、批处理,我们有**spring cloud data flow**
spring为我们想清楚了整个从开始构建应用到大型分布式应用全流程方案
创建第一个springboot程序
第一种:在官网下载jar包导入IDEA
第二种:直接在IDEA创建:
IDEA中新建项目Spring Initializr
选择依赖dependencies:web-->spring web
自动装配原理
pom.xml
*spring-boot-dependencies : 核心依赖在父工程中
*我们在写或者引入一些springboot依赖的时候,不需要指定版本,就因为有这些版本仓库
启动器:
就是springboot的启动场景。比如spring-boot-starter-web,他就会自动导入web环境所有的依赖
springboot会将所有的功能场景,都变成一个个启动器
主程序:包括注解和run
注解:
@SpringbootApplication : 标注这个类是一个springboot的应用:启动类下的所有资源被导入
@SpringBootConfiguration : springboot的配置
@Configuration:spring配置类
@Component:说明这也是一个spring的组件
@EnableAutoConfiguration : 自动配置
@AutoConfigurationPackage :自动配置包
@Import(AutoConfigurationPackages.Registrar.class) :自动配置 包注册
@Import
(AutoConfigurationImportSelector.class) :自动导入包的核心--
AutoConfigurationImportSelector:自动导入选择器,选择了什么东西--
getAutoConfigurationEntry():获得自动配置的实体,调用了下面获取候选配置
getCandidateConfigurations() :获取候选的配置----
protected Class getSpringFactoriesLoaderFactoryClass(){
return EnableAutoConfiguration.class;
//标注了EnableAutoConfiguration注解的类
}
public static List loadFactoryNames() :获取所有的加载配置
loadSpringFactories() --
项目资源:classLoader.getResources(FACTORIES_RESOURCE_LOCATION)--
**“META-INF/spring.factories ” :从这里获取配置**
--在spring-boot-autoconfigure-2.2.0.RELEASE.jar包里
---在**META-INF/spring.factories:所有的自动配置类都在这里了**!!!---
思考:为什么这么多自动配置为什么有的没有生效?需要导入对应的start才能有作用---
核心注解:@ConditionalOnXXX:如果这里面的条件都满足,才会生效
系统资源:ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)
从这些资源中遍历了所有的nexrElement(自动配置),遍历完成后封装为properties供我们使用
@ComponentScan :扫描当前主启动类同级的包
结论:springboot所有的自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面,
但是不一定生效,要判断条件是否成立,只要导入了对应的start,
就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功了。
1:springboot在启动的时候,从类路径下/META-INF/spring.factories获取指定的值
2:将这些自动配置的类导入容器,自动配置就会生效
3:整个JavaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.2.0.RELEASE.jar这个包下
4:它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器
5:容器中也会存在非常多的xxxAutoConfiguration的文件(@Bean),
就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Configuration,javaConfig
6:有了自动配置类,免去了我们手动编写配置文件的工作
run:
SpringApplication类和run方法
*SpringApplication.run(Springboot01HelloworldApplication.class,args);
SpringApplication这个类做了四个事情:
1:推断应用的类型是普通项目还是web项目
2:查找并加载所有可用初始化器,设置到initializers属性中
3:找出所有的应用程序监听器,设置到listeners属性中
4:推断并设置main方法的定义类,找到运行的主类
run方法
springboot的主配置文件:
application.properties 语法:key=value
官方推荐改成application.yaml 语法:key 空格 value
配置文件的作用:修改springboot自动配置的默认值,
因为springboot在底层都给我们自动配置好了。
yaml语法:
对空格要求高!注入到我们的配置类中
*普通的key-value:
name: vllos
*对象:
student:
name: vllos
age:3
*行内写法:
student: {name:vllos,age:3}
*数组:
pets:
-cat
-dog
-pig
pets: [cat,dog,pig]
相较于properties语法:只能存键值对
student.name = vllos
student.age = 3
yaml:给属性赋值的方式:
建实体类Person在里面加注解:
@Component:注册bean
@ConfigurationProperties(prefix = "person")
//通过这个注解把实体类Person和.yaml里面的参数person绑定起来
爆红的解决:添加依赖到pom.xml
.......
*@ConfigurationProperties作用:将配置文件中配置的每一个属性的值,映射到这个组件中;
告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定
参数:prefix = "person" :
将配置文件中的person下面的所有属性一一对应
只有这个组件是容器中的组件,才能使用容器提供的
@ConfigurationProperties功能
yaml里:
建测试类:
@Atutowired
privatre Person person;
@Test
.....
结论:配置yaml和配置properties都可以获取到值,在某个业务中,
只需要获取配置文件中的某个值,可以使用@value,
如果专门编写了一个JavaBean来和配置文件进行映射,
就直接使用@congifurationProperties
yaml支持松散绑定:比如我的yaml中写的last-name,这个和lastName是一样的,-后面跟着的字母默认是大写的,这就是松散绑定。
yaml支持JSR303数据效验:这个就是我们可以在字段是增加一层过滤器验证,可以保证数据的合法性
@validated :spring-boot中可以使用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。
如:加上@Email(),下面的值都得是邮箱格式。或者@Email("邮箱格式错误")
JSR303格式可以百度查找
yaml支持复杂类型封装:yaml中可以封装对象,使用properties对应的@value就不支持。
主配置文件.yaml可以存在的位置(4种):
file:./config //项目路径下的config文件夹配置文件
file:./ //项目路径下配置文件
classpath:/config/ //资源路径下的config文件夹配置文件
classpasth:/ //资源路径下配置文件
优先级:由高到低
多环境切换:
profile是spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境
方式一:多配置文件,用.proeprties
我们在主配置文件编写的时候,文件名可以是application-{profile}.properties/yml,用来指定多个环境版本。
例如:application-test.properties代表测试环境配置 application-dev.properties代表开发环境配置
但是springboot并不会直接启动这些配置文件,它默认使用application.properties主配置文件;
我们需要通过一个配置来选择需要激活的环境
比如在配置文件中指定使用dev环境,我们可以通过设置不同的端口号进行测试;
我们启动springooot,就可以看到已经切换到dev下的配置了;
spring.profiles.active = dev
server.port:8081
方式二:yml的多文档块
和properties配置文件一样,但是不用去创建多个配置文件,更加方便
直接在一个配置文件里写多个环境:
server:
port: 8081 //主配置文件
spring:
profiles:
active:dev //主配值文件用dev环境
--- //分割线
server:
port: 8082
spring:
profiles: dev //dev配置环境
---
server:
port: 8083
spring:
profiles: test //test配置环境
自动装配原理再理解
配置文件—联系—spring.factories
在我们这个配置文件中能配置的东西,都存在一个固有的规律:
xxxAutoConfiguration:默认值 xxxProperties 和配置文件绑定,我们就可以使用自定义的配置了。
总结:
1:springboot启动会加载大量的自动配置类
2:我们看我们需要的功能有没有再springboot默认写好的自动配置类中
3:我们再来看这个自动配置类中到底配置了哪些组件(只要我们要用的组件存在其中,我们就不需要再手动配置了)
4:给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
xxxxAutoConfiguration :自动配置类;给容器中添加组件
xxxxProperties:封装配置文件中相关属性
可以通过debug:true来查看,哪些自动配置生效了,哪些没有生效!
springboot到底帮我们配置了什么?
xxxxAutoConfiguration..向容器中自动配置组件
我们能不能进行修改?扩展?
xxxxProperties:自动配置类,装配配置文件中自定义的一些内容
SpringBoot Web开发:
要解决的问题:
导入静态资源
首页
jsp,模板引擎Thymeleaf
装配扩展SpringMVC
增删改查
拦截器
国际化
新建IDEA项目,建vllos\controller\HelloController.java
@RestController
public class HelloController{
@GetMapping("/hello")
public String hello(){
return "hello,world";
}
}
浏览器打开:
localhost:8080\hello
扒源码:在***WebMvcAutoConfiguration.java***下找
1:导入静态资源:
静态资源目录有四个:public resources static templates
在templates目录下的所有页面,只能通过controller来跳转和需要模板引擎的支持thymeleaf
在springboot,我们可以使用以下方式处理静态资源
webjars(不推荐) localhost:8080/webjars
public、static、resources 可以把静态资源放在这三个目录下,都在resources目录下 localhost:8080访问
优先级:resources>static(默认)>public
2:首页定制:
在static\index.html
图标定制:copy 图片.ico 到public或者static下试试 访问localhost:8080
在application.properties中写 spring.mvc.favicon.enable=false
3:thymeleaf模板引擎:
前端交给我们的页面,是html页面。如果是我们以前开发,需要把它们转成jsp页面,
jsp页面的好处就是当我们查出一些数据转发到jsp页面以后,我们可以用jsp轻松实现数据的显示,及交互等。
jsp支持非常强大的功能,包括能写java代码,但是,我们现在的情况:springboot这个项目首先是以jar的方式,不是war。
第二我们用的还是嵌入式的tomcat,所以默认是不支持jsp的。
不支持jsp,如果我们直接用纯静态页面的方式,那给我们开发会带来非常大的麻烦,
所以springboot推荐使用模板引擎。模板引擎很多,jsp就是一个模板引擎,还有用的多的freemarker,包括thymeleaf
引入Thymeleaf依赖:
https://docs.spring.io/spring-boot/docs/2.3.2.RELEASE/reference/htmlsingle/
<!--thymeleaf--->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java9time</artifactId>
</dependency>
写在templates下面!
格式.html
在.html中:
<html lang="en" xmlns:th="http://www.thymeleaf.org"> //导入约束
语法:所有的html元素都可以被thymeleaf替换接管: th:元素名
如:
<div th:text="${msg}"></div>
<h3 th:each="user:${users}" th:text="${user}"></h3>
建vllos\controller\IndexController.java
@Controller
public class IndexController{
@RequestMapping("/test")
public String test(Model model){
model.addAttribute("msg","hello,springboot
");
model.addAttribute("users",Arrays.asList("vllos","qintianzhu"));
return "test";
}
}
结论:只要需要使用thymeleaf,只需要导入对应的依赖就可以了。我们将html放在我们的templates目录下即可。
4:扩展装配springMVC javaconfig
在测试前,我们需要知道springboot对我们的springMVC还做了哪些配置,包括如何扩展、如何定制。途径1:源码分析 途径2:官方文档
地址:https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-
developing-web-applications.html
如果要**扩展springMVC**:
官方建议我们这样做:
**@Configuration** //只要标注这个注解就可以
如:
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
//视图跳转
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/vllos").setViewName("test");
}
}
员工管理系统
百度:bootstarap模板
1:首页配置:
1:注意点:所有页面的静态资源都需要使用thymeleaf接管
2:url:·\@{}
2:页面国际化:
1:我们需要配置i18n文件
2:我们如果需要在项目中进行按钮自动切换,我们需要自定义一个组件LocaleResovler
3:记得将自己写的组件配置到spring容器 @Bean
4:#{}
3:登录功能+拦截器
4:员工列表展示
1:提取公共页面
1:th:fragment="sidebar"
2:
3:如果要传递参数,可以直接使用()传参,接收判断即可
2:列表循环展示
5:添加员工
1:按钮提交
2:跳转到添加页面
3:添加员工成功
4:返回首页
6:CRUD
7:404
前端:
模板:别人写好的拿来改成自己需要的 百度:bootstrap后端模板
框架:组件:自己组合拼接 Bootstrap,Layui,semantic-ui
后台模板:x-admin百度
1:有一套自己熟悉的前端后台模板:工作必要!x-admin
2:前端界面:至少自己能够通过前端框架,组合出来一个网站页面
-index
-about
-blog
-post
-user
3:让这个网站能够独立运行
#如何快速搭建一个web应用:
1:前端搞定:
2:设计数据库 (数据库设计难点!)
3:前端能够自动运行,独立化工程
4:数据接口对接:json
5:前后端联调测试
JDBC
Mybatis :(重点)
Druid :(重点)
Shiro :安全 (重点)
Spring Security :安全 (重点)
异步任务,邮件发送,定时任务
Swagger
Dubbo+Zookeeper
对于数据访问层,无论是SQL还是NOSQL,SpringBoot底层都是采用Spring Data的方式进行统一处理。SpringData也是Spring中与Springboot、SpringCloud齐名的知名软件
###整合JDBC
1:导依赖
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
<!--JDBC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--MySQL-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2.连接MySQL
3.application.yaml
spring:
datasource:
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
#假如时区报错,就增加一个时区的配置就OK了:severTimezone=Asia/Shanghai
url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
4:test/Springboot04DataApplicationTests.java
@SpringBootTest
class Springboot04DataApplicationTests {
@Qualifier("dataSource")
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
//查看一下默认的数据源:class com.zaxxer.hikari.HikariDataSource
System.out.println(dataSource.getClass());
//获得数据库连接
Connection connection = dataSource.getConnection();
System.out.println(connection);
//xxxxTemplate:SpringBoot已经配置好模板bean,拿来即用 CRUD
//关闭
connection.close();
}
}
5:vllos/controller/JDBCController.java
@RestController
public class JDBCController {
@Autowired
JdbcTemplate jdbcTemplate;
//查询数据库的所有信息
//没有实体类,数据库中的东西,怎么获取? Map
@GetMapping("/userList")
public List<Map<String ,Object>> userList(){
String sql = "select * from user";
List<Map<String, Object>> list_maps = jdbcTemplate.queryForList(sql);
return list_maps;
}
@GetMapping("/addUser")
public String addUser(){
String sql = "insert into mybatis.user(id,username,birthday,sex,address)
values(51,'薛凯琪',2018-03-08 11:44:00,'女'','香港')";
jdbcTemplate.update(sql);
return "update-ok";
}
@GetMapping("/updateUser/{id}")
public String updateUser(@PathVariable("id") int id){
String sql = "UPDATE mybatis.user SET username=?,birthday=?,sex=?,address=? where id="+id;
//封装
Object[] objects = new Object[4];
objects[0] = "邓紫棋";
objects[1] = "2019-03-08 11:44:00";
objects[2] = "女";
objects[3] = "香港";
jdbcTemplate.update(sql);
return "updateUser-ok";
}
@GetMapping("/deleteUser/{id}")
public String deleteUser(@PathVariable("id") int id){
String sql = "delete from mybatis.user where id =?";
jdbcTemplate.update(sql,id);
return "deleteUser-ok";
}
}
6:浏览器访问:
浏览器访问:localhost:8080
浏览器访问:localhost:8080/updateUser
....
###整合Druid数据源
druid数据库连接池结合了C3P0、DBCP等DB池的优点,同时加入了日志监控。
可以很好的监控DB池连接和SQL的执行情况。
Springboot2.0以上默认使用Hikari数据源,可以说Hikari与Driud都是当前JavaWeb上最优秀的数据源。
(HikariDataSource号称JavaWeb当前速度最快的数据源,相比于传统的C3P0、DBCP、Tomcatjdbc等连接池更加优秀)
1:引入数据源,添加依赖,可以从Maven仓库官网Maven Repository中获取
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
2:appolication.yaml添加
type:com.alibaba.druid.pool.DruidDataSource
#SpringBoot默认是不注入这些属性值的,需要自己绑定
#Druid数据源专有配置
initialSize:5
minIdle:5
maxActive:20
maxWait:60000
timeBetweenEvictionRunsMillis:60000
minEvictableIdleTimeMillis:300000
validationQuery:SELECT 1 FROM DUAL
testWhileIdle:true
testOnBorrow:false
testOnReturn:false
poolPreparedStatements:true
#配置监控统计拦截的filters: stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException:org.apache.log4j.Priority
#则导入log4j 依赖即可,Maven地址:https://mvnrepository.com/artifact/log4j/log4j
filters:stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize:20
useGlobalDataSourceStat:true
connectionProperties:druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
3:导入log4j依赖
4:vllos/config/DruidConfig.java
@Configuration
public class DruidConfig{
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druidDataSource(){
return new DruidDataSource();
}
//后台监控:相当于web.xml, ServletRegistrationBean
//因为spring boot内置了servlet容器,所以没有web.xml,替代方法:ServletRegistrationBean
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");
//后台需要有人登陆,账号密码配置
HashMap<String,String> initParameters =new HashMap<>();
//增加配置
initParameters.put("loginUsername","admin");//登录key 是固定的
initParameters.put("loginPassword","123456");
//允许谁可以访问
initParameters.put("allow","");
//禁止谁能访问
//initParameters.put("","");
bean.setInitParameters(initParameters);//设置初始化参数
return bean;
}
//filter
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
//可以过滤哪些请求?
Map<String,String> initParameters = new HashMap<>();
//这些东西不进行统计
initParameters.put("exclusions","*.ji*.css,/druid/*");
bean.setInitParameters(initParameters);
return bean;
}
}
###整合Mybatis
整合包:mybatisi-spring-boot-starter
1:导依赖
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
2: application.properties
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai&userUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
3:连接mysql
4:导Lombok依赖
5:vllos/pojo/User.java
//实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User{
private int id;
private String username;
private Date birthday;
private boolean sex;
private String address;
}
//id,username,birthday,sex,address
//(51,'薛凯琪',2018-03-08 11:44:00,'女'','香港')";
6:vllos/mapper/UserMapper.java --interface
@Mapper //这个注解表示这是一个mybatis的mapper类
@Repository
public interface UserMapper{
List<User> queryUserList();
User queryUserById(int id);
int addUser(User user);
int updateUser(User user);
int deleteUser(int id);
}
7:在application.properties中整合mybatis
// #整合mybatis
mybatis.type-aliases-package=com.vllos.pojo
mybatis.mapper-localtions=classpath:mybatis/mapper/*.xml
8:在resources/mybatis/mapper/UserMapper.xml
//去mybatis中文文档找
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vllos.mapper.UserMMapper"> //这里要绑上自己的UserMapper
<select id="queryUserList" resultType="User" useCache="true"> //useCache="true":开启缓存
select * from user
</select>
<select id="queryUserById" resultType="User">
select * from user where id = #{
id}
</select>
<insert id="addUser" parameterType="User">
insert into user (id,username,birthday,sex,address) values(#{
id},#{
username},#{
birthday},#{
sex},#{
address})
</insert>
<update id="updateUser" parameterType="User">
update user set username=#{
username},birthday=#{
birthday},sex=#{
sex},address=#{
address} where id = #{
id}
</update>
<delete id="deleteUser" parameterType="User">
delete from user where id = #{
id}
</delete>
</mapper>
9:业务层
10:vllos/controller/UserController.java //controller层就是整合数据,前端来这里拿
@RestController
public class UserController {
@Autowired
private UserMapper userMapper;
@GetMapping("queryUserList")
public List<User> queryUserList(){
List<User> userList = userMapper.queryUserList();
return userList;
}
@GetMapping("/addUser")
public String addUser(){
userMapper.addUser(new User(51,'薛凯琪',2018-03-08 11:44:00,'女'','香港'));
return "ok";
}
@GetMapping("/updateUser")
public String updateUser(){
userMapper.updateUser(new User(51,'薛凯琪',2018-03-08 11:44:00,'女'','香港'));
return "ok";
}
@GetMapping("/deleteUser")
public String deleteUser(){
userMapper.deleteUser(51);
return "ok";
}
}
总结:
步骤:
1:导包
2:配置文件
3;mybatis配置
4:编写sql
5:业务层调用dao层
6:controller调用service层
M:数据和业务
V:HTML
C:交接
###SpringSecurity(安全)
框架
在web开发中,安全第一位 过滤器 拦截器
做网站:安全应该在设计之初就得考虑
authentication 、 access-contrl
功能权限
访问权限
菜单权限
以前用拦截器、过滤器来实现,有大量的原生代码~ 冗余
SpringSecurity是针对spring项目的安全框架,也是spring boot底层安全模块默认的技术选型,
他可以实现强大的web安全控制,对于安全控制,我们仅需要引入spring-boot-starter-security模块,进行少量的配置
即可实现强大的安全管理。
记住这几个类:
WebSecurityConfigurerAdapter:自定义Security策略
AuthenticationManagerBuilder:自定义认证策略
@EnableWebSecurity:开启WebSecurity模式
SpringSecurity的两个主要目标是“认证”和“授权”(访问控制)
“认证”(Authentication)
“授权”(Authorization)
这个概念是通用的,而不是只在SpringSecurity中存在
前期准备:
1:引入Thymeleaf依赖:
2:导入静态资源
3:application.properties
spring.thymeleaf.cache=false
4:vllos/controller/RouterController.java
@Controller
public class RouterController {
@RequestMapping({
"/","/index"})
public String index(){
return "index";
}
@RequestMapping("/toLogin")
public String toLogin(){
return "views/login";
}
@RequestMapping("/level1/{id}")
public String level1(@PathVariable("id") int id){
return "views/level1"+id;
}
@RequestMapping("/level2/{id}")
public String level2(@PathVariable("id") int id){
return "views/level2"+id;
}
@RequestMapping("/level3/{id}")
public String level3(@PathVariable("id") int id){
return "views/level1"+id;
}
}
1:导入Security依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2:vllos/config/SecurityConfig.java
//这里用的是AOP横切 相当于拦截器,但是比拦截器简单多了
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//链式编程
//授权:HttpSecurity
@Override
protected void configure(HttpSecurity http ) throws Exception {
//首页所有人可以访问,功能页只有对应有权限的人才能访问
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
//没有权限默认会导登录页面,需要开启登录的页面
//定制登录页面:loginPage("/toLogin")
http.formLogin().loginPage("/toLogin").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login");
//防止网站攻击:csrf 默认是开启的
http.csrf().disable();//关闭csrf功能,登录失败可能存在的原因
//注销,开启了注销功能
http.logout().logoutSuccessUrl("/");
//开启记住我功能:cookie 默认保存两周
http.rememberMe();
}
//认证:springboot 2.1.x 可以直接使用
//密码编码:PasswordEncoder
//在Spring Secutiry5.0+ 新增了很多的加密方法
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
//这些数据正常应该从数据库中读
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("vllos").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");
}
}
3:导入security-thymeleaf整合包
<!--security-thymeleaf整合包-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
4:pom.xml里项目中
添加:
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
spring-boot-starter-parent降版本:
2.0.9
5:改index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>首页</title>
<!--semantic-ui-->
<link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
<link th:href="@{/qinjiang/css/qinstyle.css}" rel="stylesheet">
</head>
<body>
<!--主容器-->
<div class="ui container">
<div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
<div class="ui secondary menu">
<a class="item" th:href="@{/index}">首页</a>
<!--登录注销-->
<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=""></span>-->
</a>
</div>
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/logout}">
<i class="sign-out icon"></i> 注销
</a>
</div>
<!--已登录
<a th:href="@{/usr/toUserCenter}">
<i class="address card icon"></i> admin
</a>
-->
</div>
</div>
</div>
<div class="ui segment" style="text-align: center">
<h3>Spring Security Study by 秦疆</h3>
</div>
<div>
<br>
<div class="ui three column stackable grid">
<!--菜单根据用户的角色动态的实现-->
<div class="column" sec:authorize="hasRole('vip1')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 1</h5>
<hr>
<div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
<div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
<div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip2')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 2</h5>
<hr>
<div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
<div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
<div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip3')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 3</h5>
<hr>
<div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
<div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
<div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script th:src="@{/qinjiang/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/qinjiang/js/semantic.min.js}"></script>
</body>
</html>
6:改login.xml
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>登录</title>
<!--semantic-ui-->
<link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
</head>
<body>
<!--主容器-->
<div class="ui container">
<div class="ui segment">
<div style="text-align: center">
<h1 class="header">登录</h1>
</div>
<div class="ui placeholder segment">
<div class="ui column very relaxed stackable grid">
<div class="column">
<div class="ui form">
<form th:action="@{/login}" method="post">
<div class="field">
<label>Username</label>
<div class="ui left icon input">
<input type="text" placeholder="Username" name="user">
<i class="user icon"></i>
</div>
</div>
<div class="field">
<label>Password</label>
<div class="ui left icon input">
<input type="password" name="pwd">
<i class="lock icon"></i>
</div>
</div>
<div class="field">
<input type="checkbox" name="remember">记住我
</div>
<input type="submit" class="ui blue submit button"/>
</form>
</div>
</div>
</div>
</div>
<div style="text-align: center">
<div class="ui label">
</i>注册
</div>
<br><br>
<small>blog.kuangstudy.com</small>
</div>
<div class="ui segment" style="text-align: center">
<h3>Spring Security Study by 秦疆</h3>
</div>
</div>
</div>
<script th:src="@{/qinjiang/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/qinjiang/js/semantic.min.js}"></script>
</body>
</html>
7:浏览器访问
localhost:8080/
localhost:8080/toLogin
8:未完全解决登录注销按钮转换
和主页权限控制
Shiro:
和SpringSecurity一样
Apache Shiro是一个Java的安全(权限)框架
可以在JavaSE环境和JavaEE环境下用
可以完成:认证、授权、加密、会话管理、web集成、缓存等
下载地址:http://shiro.apache.org
Subject;应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject,
Subject代表了当前的用户,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,
如网络爬虫、机器人等,与Subject的所有交互都会委托给SecurityManager;Subject其实是一个门面,
SecurityManager才是实际的执行者
SecurityManager:安全管理器,即所有与安全有关的操作都会与securitymanager交互,并且它管理着所有的Subject,
可以看出它是Shiro的核心,负责与Shiro的其他组件进行交互,它相当于SpringMVC的DispatcherServlet的角色
Realm:Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager需要验证用户身份,
那么它需要从Realm获取相应的用户进行比较,来确定用户的身份是否合法;也需要从Realm得到用户相应的角色、
权限,进行验证用户的操作是否能够进行,可以把Realm看成DataSource
1:导入依赖
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
<!--configure Logging-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
2:配置文件
//https://github.com/apache/shiro
//导入log4j:resources/log4j.properties
log4j.rootLogger=INFO, stdout
log4j.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:resources/shiro.ini
[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
3:快速开始:java/Quickstart.java
/*
* 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.ini.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.lang.util.Factory;
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):
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
// 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(securityManager);
// 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);
}
}
subject源码分析:
Subject currentUser = SecurityUtils.getSubject(); // 获取当前的用户对象Subject
Session session = currentUser.getSession(); //通过当前用户拿到Session
currentUser.isAuthenticated() //判断当前用户是否被认证
currentUser.getPrincipal() //获得当前用户的认证
currentUser.hasRole //是否拥有这个角色,拥有这个角色就会....
currentUser.isPermitted() //获得当前用户的权限
currentUser.logout() //注销
###SpringBoot整合Shiro
1:环境搭建
1:导thymeleaf依赖
<!--thymeleaf模板-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
//我们要使用thymeleaf代码提示:可以在html文件中导入命名空间的约束:
xmlns:th="http://www.thymeleaf.org"
2:resources/templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
</body>
</html>
3:vllos/controller/MyController.java
@Controller
public class MyController {
@RequestMapping({
"/","/index"})
public String toIndex(Model model){
model.addAttribute("msg","hello,Shiro");
return "index";
}
}
4:导包:spring-boot-starter-web和shiro-spring
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--shiro整合spring的包-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
5:vllos/config/ShiroConfig.java和UserRealm.java
1:UserRealm.java
//自定义的UserRealm
public class UserRealm extends AuthorizingRealm{
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行了=>授权");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=>认证");
return null;
}
}
2:ShiroConfig.java
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean
getShiroFilterFactoryBean(@Qualifier("securityManager")
DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean ShiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.serSecurityManager(defaultWebSecurityManager);
return bean;
}
//DafaultWebSecurityManager
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm 对象,需要自定义
@Bean
public UserRealm userRealm{
return new UserRealm();
}
}
6:resources/templates/add.html和update.html
1:add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>add</h1>
</body>
</html>
2:update.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>update</h1>
</body>
</html>
7:在MyController.java中添加:
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/update")
public String update(){
return "user/update";
}
2:Shiro实现登录拦截
1:写一个登录页面:templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录</h1>
<hr>
<form action="">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="text" name="password"></p>
<p><input type="submit"></p>
</form>
</body>
</html>
2:在MyController.java中加:
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
3:在ShiroConfig.java中添加:
//设置安全管理器
bean.serSecurityManager(defaultWebSecurityManager);
//以下为添加的代码:
//添加shiro的内置过滤器
/*
* anon:无需认证就可以访问
* authc:必须认证才能访问
* user:必须拥有记住我功能才能用
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
* */
Map<String,String> filterMap = new LinkedHashMap<>();
filterMap.put("/user/*","authc");
bean.setFilterChainDefinitionMap(filterMap);
//设置登录的请求
bean.setLoginUrl("/toLogin");
3:实现用户认证
1:在MyController.java中添加:
@RequestMapping("/login")
public String login(String username,String password){
//获取当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try{
subject.login(token);//执行登录方法,如果没有异常就说明OK了
return "index";
}catch(UnknownAccountException e){
//用户名不存在
model.addAttribute("msg","用户名错误");
return "login";
}catch (IncorrectCredentialsException e){
//密码不存在
model.addAttribute("msg","密码错误");
return "login";
}
}
2:login里面添加标签:
<p th:text="${msg}" style="color: red;"></p>
3:在UserRealm.java中添加:
System.out.println("执行了=>认证");
//以下为添加代码:
//用户名,密码 数据中取
String name = "root";
String password = "123456";
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
if(!userToken.getUsername().equals(name)){
return null;//抛出异常:UnknownAccountException
}
//密码认证:Shiro做
return new SimpleAuthenticationInfo("",password,"");
4:Shiro整合Mybatis
1:导依赖:
<!--subject:用户
securityManager :管理所有用户
Realm:连接数据-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connerctor-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!--引入Mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
2:resources/application.yaml
spring:
datasource:
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
#假如时区报错,就增加一个时区的配置就OK了:severTimezone=UTC
url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&
characterEncoding=utf-8&serverTimezone=Asia/Shanghaicom
.alibaba.druid.pool.DruidDataSource
type: com.alibaba.druid.pool.DruidDataSource
#SpringBoot默认是不注入这些属性值的,需要自己绑定
#Druid数据源专有配置
initialSize:5
minIdle:5
maxActive:20
maxWait:60000
timeBetweenEvictionRunsMillis:60000
minEvictableIdleTimeMillis:300000
validationQuery:SELECT 1 FROM DUAL
testWhileIdle:true
testOnBorrow:false
testOnReturn:false
poolPreparedStatements:true
#配置监控统计拦截的filters: stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException:org.apache.log4j.Priority
#则导入log4j 依赖即可,Maven地址:https://mvnrepository.com/artifact/log4j/log4j
filters:stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize:20
useGlobalDataSourceStat:true
connectionProperties:druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
3:连接MySQL
4:application.properties:
mybatis.type-aliases-package=com.vloos.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
5:vllos/pojo
1:马上去添加lombok依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
2:pojo/User.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
....
}
6:vllos/mapper/UserMapper.java
@Repository
@AllArgsConstructor
@NoArgsConstructor
public interface UserMapper {
public User queryUserByName(String name);
}
7:resources/mapper/UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vllos.mapper.UserMapper">
<select id="queryUserByName" parameterType="String" resultType="User">
select * from mybatis.user where name = #{
name}
</select>
</mapper>
8:vllos/service/UserService.java
public interface UserService {
public User queryUserByName(String name);
}
9:vllos/service/UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Override
public User queryUserByName(String name) {
return userMapper.queryUserByName(name);
}
}
10:test/../ShiroSpringbootApplicationTests.java
@SpringBootTest
class ShiroSpringbootApplicationTests {
@Autowired
UserServiceImpl userService;
@Test
void contextLoads() {
System.out.println(userService.queryUserByName("vllos"));
}
}
11:UserRealm.java 添加:
1:
@Autowired
UserService userService;
2:
if(!userToken.getUsername().equals(name)){
return null;//抛出异常:UnknownAccountException
}
//密码认证:Shiro做
return new SimpleAuthenticationInfo("",password,"");
改成:
//连接真实数据库
User user = userService.queryUserByName(userToken.getUsername());
if (user==null){
//没有这个人
return null;
}
//密码认证:Shiro做
return new SimpleAuthenticationInfo("",user.getPwd(),"");
5:Shiro请求授权实现
1:ShiroConfig.java中:
//拦截
Map<String,String> filterMap = new LinkedHashMap<>();
//添加下面代码
//授权
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
2:ShiroConfig.java中:
//未授权页面
bean.setUnauthorizedUrl("/noauth");
3:MyController.java中:
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized(){
return "未授权无法访问此页面";
}
4:UserRealm.java中:
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("user:add");
return info;
5:在MySQL中增加一列:用来实现用户拥有的权限
如:perms
user:add
user:update
User.java中也要相应改
6:UserRealm.java
//拿到当前登录的这个对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal(); //拿到User对象
//设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
2:
认证下面改:
return new SimpleAuthenticationInfo("",user.getPwd(),"");
改成:
return new SimpleAuthenticationInfo("user",user.getPwd(),"");
6:Shiro整合thymeleaf
1:导shiro-thymeleaf整合包
!--shiro-thymeleaf整合-->
<dependency>
<groupId>org.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
</dependency>
2:ShiroConfig.java中添加:
//整合shiroDialect:用来整合shiro thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
3:index.xml
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<div th:if="${session.loginUser==null}">
<a th:href="@{/toLogin}">登录</a>
</div>
<p th:text="${msg}"></p>
<hr>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
</body>
</html>
4:在UserRealm.java中添加:
if (user==null){
//没有这个人
return null;
}
//以下为添加代码
Subject currentSubject = SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser",user);
shiro-springboot码:
1:pom.xml
```java
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.2.RELEASE
com.vllos
shiro-springboot
0.0.1-SNAPSHOT
shiro-springboot
Demo project for Spring Boot
9
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connerctor-java
org.projectlombok
lombok
1.18.12
com.alibaba
druid
1.1.12
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.1
log4j
log4j
1.2.12
org.apache.shiro
shiro-spring
1.4.1
org.github.theborakompanioni
thymeleaf-extras-shiro
org.thymeleaf
thymeleaf-spring5
org.thymeleaf.extras
thymeleaf-extras-java8time
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework.boot
spring-boot-maven-plugin
```
2:vllos/config:
1:ShiroConfig.java
```java
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(
@Qualifier("securityManager")
DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean ShiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.serSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
* anon:无需认证就可以访问
* authc:必须认证才能访问
* user:必须拥有记住我功能才能用
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
* */
//拦截
Map filterMap = new LinkedHashMap<>();
//授权,正常情况下,没有授权会跳到未授权的页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
//设置登录的请求
bean.setLoginUrl("/toLogin");
//未授权页面
bean.setUnauthorizedUrl("/noauth");
return bean;
}
//DafaultWebSecurityManager
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm 对象,需要自定义
@Bean
public UserRealm userRealm{
return new UserRealm();
}
//整合shiroDialect:用来整合shiro thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
```
2:UserRealm.java
```java
//自定义的UserRealm
public class UserRealm extends AuthorizingRealm{
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行了=>授权");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("user:add");
return info;
//拿到当前登录的这个对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();//拿到User对象
//设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=>认证");
//用户名,密码 数据中取
String name = "root";
String password = "123456";
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
//连接真实数据库
User user = userService.queryUserByName(userToken.getUsername());
if (user==null){//没有这个人
return null;
}
Subject currentSubject = SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser",user);
//可以做加密:MD5 MD5盐值加密
//密码认证:Shiro做
return new SimpleAuthenticationInfo("user",user.getPwd(),"");
}
}
```
3:vllos/controller
MyController.java
```java
@Controller
public class MyController {
@RequestMapping({"/","/index"})
public String toIndex(Model model){
model.addAttribute("msg","hello,Shiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/update")
public String update(){
return "user/update";
}
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
@RequestMapping("/login")
public String login(String username,String password){
//获取当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try{
subject.login(token);//执行登录方法,如果没有异常就说明OK了
return "index";
}catch(UnknownAccountException e){//用户名不存在
model.addAttribute("msg","用户名错误");
return "login";
}catch (IncorrectCredentialsException e){//密码不存在
model.addAttribute("msg","密码错误");
return "login";
}
}
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized(){
return "未授权无法访问此页面";
}
}
```
4:vllos/mapper
UserMapper.java
```java
@Repository
@AllArgsConstructor
@NoArgsConstructor
public interface UserMapper {
public User queryUserByName(String name);
}
```
5:vllos/pojo
User.java
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
}
```
6:vllos/service
1:UserService.java
```java
import com.vllos.pojo.User;
public interface UserService {
public User queryUserByName(String name);
}
```
2:UserServiceImpl.java
```java
import com.vllos.mapper.UserMapper;
import com.vllos.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Override
public User queryUserByName(String name) {
return userMapper.queryUserByName(name);
}
}
```
7:resources/
1:application.yaml
spring:
datasource:
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
#假如时区报错,就增加一个时区的配置就OK了:severTimezone=UTC
url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&characterEncoding=utf-8&
serverTimezone=Asia/Shanghaicom.alibaba.druid.pool.DruidDataSource
type: com.alibaba.druid.pool.DruidDataSource
#SpringBoot默认是不注入这些属性值的,需要自己绑定
#Druid数据源专有配置
initialSize:5
minIdle:5
maxActive:20
maxWait:60000
timeBetweenEvictionRunsMillis:60000
minEvictableIdleTimeMillis:300000
validationQuery:SELECT 1 FROM DUAL
testWhileIdle:true
testOnBorrow:false
testOnReturn:false
poolPreparedStatements:true
#配置监控统计拦截的filters: stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException:org.apache.log4j.Priority
#则导入log4j 依赖即可,Maven地址:https://mvnrepository.com/artifact/log4j/log4j
filters:stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize:20
useGlobalDataSourceStat:true
connectionProperties:druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
2:application.properties
mybatis.type-aliases-package=com.vloos.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
8:resources/mapper
UserMapper.xml
9:resources/templates
1:index.xml
Title
首页
2:login.xml
Title
登录