文章目录

  • SpringBoot
    • 1、什么是Spring?
    • 2、Spring是如何简化Java开发的?
    • 3、什么是SpringBoot?
    • 4、微服务
      • 4.1、什么是微服务?
    • 5、第一个SpringBoot程序
      • 5.1、官网
      • 5.2、IDEA
    • 6、SpringBoot原理初探
      • 6.1、@SpringBootApplication
      • 6.2、SpringApplication
    • 7、SpringBoot配置
      • 7.1、YAML
      • 7.2、application.yaml路径
      • 7.3、多环境切换
      • 7.4、自动配置原理
    • 8、SpringBoot web开发
      • 8.1、静态资源
    • 9、Thymeleaf
    • 10、扩展SpringMVC
    • 11、web
      • 11.1、首页
      • 11.2、国际化
      • 11.3、提示
      • 11.4、拦截器
      • 11.5、代码复用
      • 11.6、显示高亮(thymeleaf传参)
      • 11.7、CRUD
      • 11.8、404
    • 12、test
    • 13、Druid数据源
    • 14、整合mybatis
    • 15、SpringSecurity(安全)
      • 15.1、权限认证
      • 15.2、注销
      • 15.3、Security与thymeleaf整合
      • 15.4、自定义登录页面
      • 15.5、记住我
    • 16、Shiro简介
    • 17、Shiro整合Mybatis
      • 171、用户认证
      • 17.2、用户授权
      • 17.3、Shiro整合thymeleaf

微服务阶段

  • javaweb:独立开发MVC三层架构的网站:原始
  • ssm:框架:简化了我们的开发流程,配置也开始较为复杂;
  • war:以上项目的打包是打war包,程序在tomcat中运行
  • spring再简化:SpringBoot打的是Jar包,内嵌了tomcat;微服务架构!
  • 服务越来越多:SpringCloud

SpringBoot

1、什么是Spring?

Spring是一个开源框架,2003年兴起的一个轻量级的Java开发框架,作者:Rod Johnson。

Spring是为了解决企业级应用开发的复杂性而创建的,简化开发

2、Spring是如何简化Java开发的?

为了降低Java开发的复杂性,Spring采用了以下4种关键策略:

1、基于POJO的轻量级和最小侵入性编程,所有东西都是bean;

2、通过IOC,依赖注入(DI)和面向接口实现松耦合;

3、基于切面(AOP)和惯例进行声明式编程;

4、通过切面和模版减少样式代码,RedisTemplate,xxxTemplate;

3、什么是SpringBoot?

​ SpringBoot就是一个javaweb的开发框架,和SpringMVC类似,对比于其他javaweb框架的好处,官方说是简化开发,约定大于配置,能迅速地开发web应用,一行代码开发一个http接口。

​ SpringBoot以约定大于配置的核心思想,帮我们进行了很多设置,多数SpringBoot应用只需要很少的Spring配置。

Spring Boot的主要优点:

  • 为所有Spring开发者更快的入门
  • 开箱即用,提供各种默认配置来简化项目配置
  • 内嵌式容器简化Web项目
  • 没有冗余代码生成和XML配置的要求

4、微服务

4.1、什么是微服务?

​ 微服务是一种架构风格,他要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合。要说微服务架构,先得说说过去的单体应用架构。

**单体应用架构(all in one)**是指,我们将一个应用中的所有应用服务都封装在一个应用中。

​ 无论是ERP、CRM或是其它系统,都可以把数据库访问、web访问等功能放在一个war包内,

​ 这样做的好处是,易于开发和测试,也十分方便部署;当需要扩展时,只需要将war复制多份,然后放到多个服务器上,再做个负载均衡就可以了。

​ 缺点是,哪怕我要修改一个非常小的部分,都要停掉整个服务,重新打包、部署这个应用war包。特别是对于大型应用,不可能把所有的内容都放在一个应用中,这样维护,分工合作都是问题。

微服务架构打破之前all in one的架构方式,把每个功能元素独立出来。把独立出来的功能元素动态组合,需要的功能元素才去拿来组合,需要多一些时可以整合多个功能元素。所以微服务架构是对功能元素进行复制,而没有对整个应用进行复制。

​ 好处是:

  1. 节省了调用资源;
  2. 每个功能元素的服务都是一个可替换的,可独立升级的软件代码。

5、第一个SpringBoot程序

5.1、官网

[https://spring.io/projects/spring-boot#overview]

直接在官网快速搭建一个SpringBoot项目

5.2、IDEA

添加Spring WEB依赖。

创建第一个程序

@Controller
@RequestMapping("/hello")
public class HelloController {@RequestMapping("/h1")@ResponseBodypublic String hello() {return "Hello SpringBoot";}}

6、SpringBoot原理初探

[https://www.bilibili.com/video/BV1PE411i7CV?p=12&spm_id_from=pageDriver]

自动配置:

pom.xml

  • spring-boot-dependencies:核心依赖在父工程中
  • 我们在写或者引入一些SpringBoot中依赖的时候,不需要指定版本,就因为有这些版本仓库

**启动器:**SpringBoot的启动场景

  • <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.5.6</version><scope>compile</scope>
    </dependency>
    
  • springboot会将所有的功能场景,都变成一个个的启动器。

  • 要使用什么功能,就只需找到对应的starter

主程序:

//@SpringBootApplication : 标注这个类是一个SpringBoot的应用,启动类下的所有资源被导入
@SpringBootApplication
public class Springboot01HellowordApplication {public static void main(String[] args) {//将SpringBoot应用启动,通过反射加载类的对象SpringApplication.run(Springboot01HellowordApplication.class, args);}}

6.1、@SpringBootApplication

  • @SpringBootApplication->@SpringBootConfiguration:SpringBoot的配置->@Configuration:Spring配置类->@Component:说明这也是一个Spring的组件
    
  • @SpringBootApplication->@EnableAutoConfiguration:自动导入配置->@AutoConfigurationPackage:自动配置包->@Import(AutoConfigurationPackages.Registrar.class):自动配置“包注册”@Import(AutoConfigurationImportSelector.class):自动配置导入选择
    
    • AutoConfigurationImportSelector.class中

      //获取所有的配置
      List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);->
      //获取候选的配置
      protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;
      }
      
    • 自动配置的核心文件

@SpringBootApplication :

标注这个类是一个SpringBoot的应用,启动类下的所有资源被导入

结论:SpringBoot所有自动配置都是在启动的时候扫描并加载(spring.factories),所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们的自动装配就会生效,然后就配置成功。

  1. SpringBoot在启动的时候,从类路径下/META-INF/spring.factories获取指定的值;
  2. 将这些自动配置的类导入容器,自动配置就会生效,帮我们进行自动配置;
  3. 以前我们需要自动配置的东西,springboot帮我们做了;
  4. 整合JavaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.5.6.jar这个包下;
  5. 它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器;
  6. 容器中也会存在非常多的xxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的组件;并且自动配置,@Configuration;
  7. 有了自动配置类,免去了我们手动编写配置文件的工作。

6.2、SpringApplication

//将SpringBoot应用启动,通过反射加载类的对象
SpringApplication.run(Springboot01HellowordApplication.class, args);

该方法主要由两个部分,一部分是SpringApplication的实例化,一部分是run方法的执行。

这个类主要做了以下四件事情:

  1. 推断应用的类型是普通的项目还是web项目(如果是普通项目,执行完就结束,如果是web项目,就会一直启动);
  2. 查找并加载所有可用初始化器,设置到initializers属性中;
  3. 找出所有的应用程序监听器,设置到linsteners属性中;
  4. **推断并设置main方法的定义类,找到运行的主类,因为他如果不知道主类的话,是没有办法加载的。 **

关于SpringBoot,谈谈你的理解:

  • 自动装配
  • run()
    • 推断应用的类型是普通的项目还是web项目(如果是普通项目,执行完就结束,如果是web项目,就会一直启动);
    • 推断并设置main方法的定义类,找到运行的主类,因为他如果不知道主类的话,是没有办法加载的。
    • run()里面有一些监听器,这个监听器是全局存在的,它会去获取上下文处理一些Bean,

7、SpringBoot配置

7.1、YAML

语法

SpringBoot使用一个全局的配置文件,配置文件名称是固定的

  • application.properties

    • 语法结构:key = value
  • application.yml
    • 语法结构:key:空格value

yaml可以直接给实体类赋值

  • Spring的赋值方式

    @Component
    public class Dog {@Value("旺财")private String name;@Value("3")private Integer age;
    }
    
    @SpringBootTest
    class Springboot01HellowordApplicationTests {@Autowiredprivate Dog dog;@Testvoid contextLoads() {System.out.println(dog);//Dog{name='旺财', age=3}}}
    
  • yaml给实体类赋值

    输出:

    Person{name='张三', age=18, happy=false, birth=Tue Nov 02 00:00:00 CST 2021, maps={k1=v1, k2=v2}, list=[code, music, girl], dog=Dog{name='旺财', age=3}}
    

    添加依赖即可解决爆红问题:

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional>
    </dependency>
    
  • 加载指定的配置文件

    @PropertySource(value = "classpath:application.properties")//加载指定的配置文件
    public class Person {//SPEL表达式取出配置文件中的值@Value("${name}")private String name;private Integer age;private Boolean happy;private Date birth;private Map<String,Object> maps;private List<Object> list;private Dog dog;
    }
    

@ConfigurationProperties和@Value对比:

@ConfigurationProperties @Value
功能 批量注入配置文件中的属性 一个个指定
松散绑定(松散语法) 支持 不支持
SpEL 不支持 支持
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持

松散绑定:

结论:

  • 配置yml和配置properties都可以获取到值,强烈推荐yml
  • 如果我们在某个业务中,只需要获取配置文件中的某个值,可以使用@Value
  • 如果我们专门编写了一个JavaBean来和配置文件进行映射,就直接使用@ConfigurationProperties。

7.2、application.yaml路径

application.yaml(.properties)这个文件可以放在四个不同的位置,位置不同执行的优先级不同,从上往下优先级降低:

  1. file:./config/
  2. file:./
  3. classpath:/config
  4. classpath:/

7.3、多环境切换

properties的多配置文件

我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml , 用来指定多个环境版本;

例如:

application-test.properties 代表测试环境配置

application-dev.properties 代表开发环境配置

但是Springboot并不会直接启动这些配置文件,它默认使用application.properties主配置文件

我们需要通过一个配置来选择需要激活的环境:

#比如在配置文件中指定使用dev环境,我们可以通过设置不同的端口号进行测试;#我们启动SpringBoot,就可以看到已经切换到dev下的配置了;
spring.profiles.active=dev

yaml的多文档模块

和properties配置文件中一样,但是使用yml去实现不需要创建多个配置文件,更加方便了 !这也是SpringBoot推荐使用yaml的原因。

7.4、自动配置原理

application.properties/yml配置文件到底能写什么?怎么写?

分析自动配置原理:以HttpEncodingAutoConfiguration为例解释自动配置原理

//@Configuration 表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件
@Configuration(proxyBeanMethods = false)
//启动指定类的ConfigurationProperties功能;//进入这个Properties查看,将配置文件中对应的值和HttpProperties绑定起来//并把HttpProperties加入到ioc容器中。
@EnableConfigurationProperties(ServerProperties.class)
//Spring底层@Conditional注解://根据不同的条件判断,如果满足指定的条件,整个配置类里的配置就会生效;//这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效。
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {private final Encoding properties;public HttpEncodingAutoConfiguration(ServerProperties properties) {this.properties = properties.getServlet().getEncoding();}@Bean@ConditionalOnMissingBeanpublic CharacterEncodingFilter characterEncodingFilter() {CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();filter.setEncoding(this.properties.getCharset().name());filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));return filter;}@Beanpublic LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {return new LocaleCharsetMappingsCustomizer(this.properties);}static class LocaleCharsetMappingsCustomizerimplements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {private final Encoding properties;LocaleCharsetMappingsCustomizer(Encoding properties) {this.properties = properties;}@Overridepublic void customize(ConfigurableServletWebServerFactory factory) {if (this.properties.getMapping() != null) {factory.setLocaleCharsetMappings(this.properties.getMapping());}}@Overridepublic int getOrder() {return 0;}}}

精髓

1、SpringBoot启动会加载大量的自动配置类

2、我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;

3、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)

4、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;

**xxxxAutoConfigurartion:自动配置类;**给容器中添加组件

xxxxProperties:封装配置文件中相关属性;

那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。

我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;

#开启springboot的调试类
debug=true

8、SpringBoot web开发

SpringBoot到底帮我们配置了什么?我们能不能进行修改?能修改哪些东西?能不能扩展?

答案是都是可以的。

  • xxxAutoConfiguration:向容器中自动配置组件
  • xxxProperties:自动配置类,装配配置文件中自定义的一些内容

要解决的问题:

  • 导入静态资源
  • 首页
  • 没有jsp页面,使用模板引擎Thymeleaf
  • 装配扩展SpringMVC
  • 增删改查
  • 拦截器
  • 国际化

8.1、静态资源

双击shift找到WebMvcAutoConfiguration

分析源码:WebMvcAutoConfiguration.java

//资源处理器
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {if (!this.resourceProperties.isAddMappings()) {logger.debug("Default resource handling disabled");return;}addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {registration.addResourceLocations(this.resourceProperties.getStaticLocations());if (this.servletContext != null) {ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);registration.addResourceLocations(resource);}});
}
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/","classpath:/resources/", "classpath:/static/", "classpath:/public/" };

也可以在application.properties中自定义目录,这也系统默认寻找静态资源的路径就会失效(一般不建议这样使用)。

spring.mvc.static-path-pattern=/hello/,classpath:/lei/

总结:

  1. 在SpringBoot中,可以使用以下方式处理静态资源

    • webjars localhost:8080/webjars/
    • public, static, /**, resource localhost:8080/
  2. 优先级:resource > static(默认) > public

9、Thymeleaf

[https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter ]

需要使用Thymeleaf,首先要导入对应的依赖,将html页面放在templates中。

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

模板引擎的作用就是写一个页面模板,比如有些值是动态的。

Thymeleaf所有的模板引擎写在templates下

thymeleaf的自动配置类由之前的结论可知:ThymeleafProperties

由此可知,前缀、后缀、默认编码,视图解析器,在calsspath:/templates/路径下创建一个后缀为.html的页面。

使用Thymeleaf,在html头文件中导入约束。

@RequestMapping("/test1")
public String test1(Model model) {model.addAttribute("msg", "hello SpringBoot");model.addAttribute("users", Arrays.asList("张三", "李四", "王五"));return "test";
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>Title</title></head><body><!-- 所有的html元素都可以被thymeleaf替换接管  th:元素名--><div th:text="${msg}"></div></body>
</html>
  • 取值

    <!-- 所有的html元素都可以被thymeleaf替换接管  th:元素名-->
    <div th:text="${msg}"></div>
    
  • 遍历

    <div th:each="user:${users}" th:text="${user}"></div>
    
  • 简单的表达式

    • 普通的变量:${…}
    • message表达(国际化消息):*{…}
    • url:@{…}
    • 片段表达式:~{…}

10、扩展SpringMVC

在SpringBoot中有非常多的xxxConfiguration帮助我们进行扩展配置,只要看见了这个配置,就需要特别注意,关注一下扩展了什么功能。

package com.ls.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;//如果需要扩展SpringMVC,官方建议我们这样去做
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {//视图跳转@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/lei").setViewName("test");}
}

注意:如果需要扩展SpringMVC,使用@Configuration注解之后就不能在添加@EnableWebMvc这个注解。

//如果需要扩展SpringMVC,官方建议我们这样去做,但是接管MVC的时候不能加@EnableWebMvc这个注解。
@Configuration
/*@EnableWebMvc这个注解就是导入一个类(DelegatingWebMvcConfiguration 作用:从容器中获取所有的webmvcconfigura),public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {...}只要加了这个注解容器中就存在了WebMvcConfigurationSupport但是在WebMvcAutoConfiguration中@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)ConditionalOnMissingBean意思是如果这个Bean不存在它才会生效;如果存在,那么WebMvcAutoConfiguration的全部自动配置就会全部失效*/
@EnableWebMvc
public class MyMvcConfig implements WebMvcConfigurer {//视图跳转@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/lei").setViewName("test");}
}

11、web

# 关闭默认头像spring.mvc.favicon.enable=false# 关闭thymeleaf缓存
spring.thymeleaf.cache=false# http://localhost:8080/lei/
server.servlet.context-path=/lei

首页配置

注意点:所有页面的静态资源都需要使用thymeleaf接管,@{}

11.1、首页

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><meta name="description" content=""><meta name="author" content=""><title>Signin Template for Bootstrap</title><!-- Bootstrap core CSS --><!-- static静态目录不用写 --><link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"><!-- Custom styles for this template --><link th:href="@{/css/signin.css}" rel="stylesheet"></head><body class="text-center"><form class="form-signin" action="dashboard.html"><img class="mb-4" th:src="@{/img/1.jpg}" alt="" width="72" height="72"><h1 class="h3 mb-3 font-weight-normal">Please sign in</h1><label class="sr-only">Username</label><input type="text" class="form-control" placeholder="Username" required="" autofocus=""><label class="sr-only">Password</label><input type="password" class="form-control" placeholder="Password" required=""><div class="checkbox mb-3"><label><input type="checkbox" value="remember-me"> Remember me</label></div><button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button><p class="mt-5 mb-3 text-muted">© 2017-2018</p><a class="btn btn-sm">中文</a><a class="btn btn-sm">English</a></form></body></html>

11.2、国际化

在application中配置login的目录

# 国际化配置文件的目录
spring.messages.basename=i18n/login

之后再html页面中用 th:text="#{}"或者[[#{}]]取值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3cmRSBGy-1637377596110)(D:\Typora\img\image-20211103165001770.png)]

添加中英切换的链接

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

自定义国际化组件

public class MyLocalResolver implements LocaleResolver {@Overridepublic Locale resolveLocale(HttpServletRequest request) {//获取请求中的语言参数String language = request.getParameter("l");System.out.println("language = " + language);//如果没有就使用默认的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) {}
}

将写好的组件注入到容器中(MyMvcConfig),这样自定义的国际化组件就生效了

//往容器中注入组件,自定义的国际化组件就生效了
@Bean
public LocaleResolver localeResolver() {return new MyLocalResolver();
}

注意点:

  • 需要配置i8n文件
  • 如果在项目中需要进行按钮自动切换,需要自定义一个组件LocaleResolver
  • 然后将组件配置到spring容器中@Bean
  • #{}和@{}

11.3、提示

//通过自定义的方式扩展MVC
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");registry.addViewController("/index.html").setViewName("index");//真实的页面是dashboard.html,相当于给它起了个别名。//http://localhost:8080/lei/main.htmlregistry.addViewController("/main.html").setViewName("dashboard");}//往容器中注入组件,自定义的国际化组件就生效了@Beanpublic LocaleResolver localeResolver() {return new MyLocalResolver();}
}
@RequestMapping("/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session) {//如果username不为空并且密码为123System.out.println(username + password);if (!StringUtils.isEmpty(username) && "123".equals(password)) {return "redirect:/main.html";} else {model.addAttribute("error", "用户名或密码错误");return "index";}
}
<!-- 提示错误信息 -->
<p style="color: red" th:text="${error}" th:if="${not #strings.isEmpty(error)}"></p>

11.4、拦截器

  1. 添加Session

    @RequestMapping("/login")
    public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session) {//如果username不为空并且密码为123System.out.println(username + password);if (!StringUtils.isEmpty(username) && "123".equals(password)) {session.setAttribute("loginUser", username);return "redirect:/main.html";} else {model.addAttribute("error", "用户名或密码错误");return "index";}
    }
    
  2. 实现接口

    //只要实现了HandlerInterceptor这个类就是一个拦截器
    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) {request.setAttribute("error","没有权限,请先登录!");request.getRequestDispatcher("/index.html").forward(request, response);return false;} else {return true;}}
    }
    
  3. 注册到自定义配置容器中

    @Override
    public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/","/user/login","/css/**","/img/**","/js/**");
    }
    
  4. 添加拦截器之后在访问main.htm就会提示

11.5、代码复用

在list.html和dashbard.html中有顶部和侧部导航栏都是相同的内容,看起来很繁琐,所以需要使用th:fragment来实现代码复用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bn6m0Zgl-1637377596114)(D:\Typora\img\image-20211103204802492.png)]

解决办法:

  1. 删除list的顶部导航栏

  2. 添加fragment

  3. 在list原来的位置插入

  4. 效果和原来一样

侧边栏也是一样

  1. 删除list.html相同的侧边栏

  2. 在dashboard.html的侧边添加th:fragment="leftbar"

    <nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="leftbar">
    
  3. 在list.html原来的位置导入

    <div th:insert="~{dashboard::leftbar}"></div>
    

代码复用再升级

将顶部和侧边的导航栏提取出来,放在新建的一个html中。

在需要使用的html中分别插入或者替换

11.6、显示高亮(thymeleaf传参)

需求:点击首页或者员工管理让对应按钮显示高亮,如果要传递参数,可直接使用()传参,接收判断即可。

<!-- 传递参数给组件 -->
<div th:insert="~{commons/commons::leftbar(active='list.html')}"></div>
<!-- 看传递过来的参数值是不是等于list.html?如果相等则为nav-link active(激活态),如果不等则为nav-link -->
<a th:class="${active=='list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}">

11.7、CRUD

  1. 查询所有

    @RequestMapping("/emps")
    public String list(Model model) {Collection<Employee> employees = employeeDao.getAll();model.addAttribute("emps", employees);return "emp/list";
    }
    
    <table class="table table-striped table-sm"><thead><tr><th>编号</th><th>姓名</th><th>email</th><th>性别</th><th>部门</th><th>生日</th><th>操作</th></tr></thead><tbody><tr th:each="emp:${emps}"><td th:text="${emp.getId()}"></td><td th:text="${emp.getLastName()}"></td><td th:text="${emp.getEmail()}"></td><td>[[${emp.getGender()==0?'女':'男'}]]</td><td th:text="${emp.department.getName()}"></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>
    </table>
    

    增加的时候注意:

<!-- controller接收的是一个Employee对象,所以需要提交其中的一个属性。 -->
<select class="form-control" name="department.id"><option th:each="department:${departments}" th:text="${department.getName()}" th:value="${department.getId()}"></option>
</select>

修改传参时:

<a class="btn btn-sm  btn-primary" th:href="@{/goUpdate/} + ${emp.id}">编辑</a>
或者
<a class="btn btn-sm  btn-primary" th:href="@{/goUpdate(id=${emp.getId()})}">编辑</a>

后台接收

//以restful风格传参,就要以restful风格接收
//去员工的修改页面
@RequestMapping("/goUpdate/{id}")
public String doUpdate(@PathVariable int id,Model model) {System.out.println("id========>" + id);Employee employee = employeeDao.getEmployeeById(id);model.addAttribute("employee", employee);return "/emp/update";
}

11.8、404

只需在templates下建一个error文件夹,将404.html放进去。

12、test

  1. 连接数据库

    spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8username: rootpassword: 171684
    
  2. 测试数据库连接

    @RestController
    public class JDBCController {@AutowiredJdbcTemplate jdbcTemplate;//查询数据库中所有的信息//没有实体类,数据库中的数据,从哪里获取?@GetMapping("/userList")public List<Map<String, Object>> getUser() {String sql = "select * from user";List<Map<String, Object>> user_maps = jdbcTemplate.queryForList(sql);return user_maps;}@RequestMapping("/delete/{id}")public String delete(@PathVariable("id") Integer id) {String sql = "delete from user where id = ?";jdbcTemplate.update(sql, id);return "delete";}}
    

13、Druid数据源

HikariDataSource号称Java WEB当前最快的数据源,相比于传统的c3p0,DBCP,Tomcat JDBC等连接池更加优秀。

Druid数据源

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.8</version>
</dependency>

指定需要使用的数据源:

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimeZone=UTCusername: rootpassword: 171684type: com.alibaba.druid.pool.DruidDataSource

Druid配置类

//Druid后台监控硬性配置
@Configuration
public class DruidConfig {@ConfigurationProperties(prefix = "spring.datasource")@Beanpublic DataSource dataSource() {return new DruidDataSource();}@Bean//后台监控(相当于web.xml)public ServletRegistrationBean statViewServlet() {ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");//后台需要有人登录,账号密码设置HashMap<String, String> initParameters = new HashMap<>();//增加配置//登录的loginUsername和loginPassword是固定好的,不能更改initParameters.put("loginUsername","admin");initParameters.put("loginPassword","123456");//允许谁可以访问// ""代表所有人可以访问,localhost代表只有本机可以访问initParameters.put("allow","");//禁止谁可以访问
//        initParameters.put("leishuai","120.55.168.89");//设置初始化参数bean.setInitParameters(initParameters);return bean;}
}

配置好DruidConfig之后,在浏览器访问localhost:8080/druid会显示一个登录的信息,输入设置好的用户名和密码,就进入到首页。

执行任意一条sql,测试:http://localhost:8080/userList

会记录所执行的一些SQL信息。

14、整合mybatis

springboot整合mybatis

导入依赖
<!-- mybatis整合 -->
<!-- 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.2.0</version>
</dependency>

测试连接

@SpringBootTest
class Springboot05MybatisApplicationTests {@Autowiredprivate DataSource dataSource;@Testvoid contextLoads() throws SQLException {System.out.println(dataSource.getClass());System.out.println(dataSource.getConnection());}}

声明mapper的注解@Mapper,两种方式

  • //这个注解表示了这是一个mybatis的 mapper类
    @Mapper
    public interface userMapper {}
    
  • @SpringBootApplication
    @MapperScan("com.ls.Mapper")
    public class Springboot05MybatisApplication {public static void main(String[] args) {SpringApplication.run(Springboot05MybatisApplication.class, args);}}
    

编写User的接口

//这个注解表示了这是一个mybatis的 mapper类
@Mapper
@Repository
public interface userMapper {List<User> queryUserList();User queryById(int id);int addUserUser(User user);int updateUser(User user);int deleteUser(int id);}

在resources下建立mybatis/mapper/UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace绑定一个对应的Dao/mapper接口 -->
<mapper namespace="com.ls.mapper.UserMapper"><select id="queryUserList" resultType="User">select * from user</select>
</mapper>

application.properties

# 整合mybatis
mybatis.type-aliases-package=com.ls.pojomybatis.mapper-locations=classpath:mybatis/mapper/*.xml

测试

@RestController
public class UserController {@Autowiredprivate UserMapper userMapper;@GetMapping("/userList")public List<User> queryAll() {List<User> userList = userMapper.queryUserList();for (User user : userList) {System.out.println(user);}return userList;}}

结果

15、SpringSecurity(安全)

Spring Security是针对spring项目的一个安全框架,也是SpringBoot底层安全模块默认的技术选型,它可以实现强大的web安全控制,对于安全控制,进需要引入spring-boot-start-security模块,进行少量的配置,即可实现强大的安全管理。

记住几个类:

  • WebSecurityConfigurationAdapter:自定义Sercurity策略
  • AuthenticationManagerBuiler:自定义认证策略
  • @EnableWebSecurity:开启WebSecurity模式

Spring Security的两个主要目标就是“认证”和”授权(访问控制)“

认证:Authentication

授权:Authorization

以横切(aop)的思想进去,不用改变源码还能加很多的拦截操作。

Spring Security是一个专注于为Java应用程序提供身份验证和授权的框架。与所有Spring项目一样,Spring Security的真正强大之处在于它可以很容易地扩展以满足定制需求

在web开发中,安全第一位!

安全并不是一个功能性需求。

做网站:安全应该在什么时候考虑?设计之初!

  • 漏洞,隐私泄露
  • 架构一旦确定就不好在考虑安全问题

shiro,SpringSecurity:很像,除了类不一样,名字不一样

15.1、权限认证

  1. 导入依赖

    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId><version>2.5.6</version>
    </dependency>
    
  2. 编写配置类

    @EnableWebSecurity//开启WebSecurity模式,@Enablexxx,开启某个功能
    public class SecurityConfig extends WebSecurityConfigurerAdapter {//授权protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/").permitAll().antMatchers("/level1/**").hasRole("vip1").antMatchers("/level2/**").hasRole("vip2").antMatchers("/level3/**").hasRole("vip3");//没有权限会默认跳转到登录页面,需要开启登录的页面//http://localhost:8080/loginhttp.formLogin();}
    }
    

    点击跳转功能,没有权限的人会默认跳转到登录页面

  3. 添加权限认证

    //认证
    //报错:There is no PasswordEncoder mapped for the id "null"
    //在Spring Security 5.0+版本以上新增了很多的加密方法
    //new BCryptPasswordEncoder().encode("123456") 密码加密,防止反编译破解。
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("leishuai").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3").and().withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2").and().withUser("user").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");
    }
    

    用不同的Username和Password登录,用户只能访问拥有对应权限的页面

    • 此时用root,123456登录

    • 他只能访问level1和level2底下的页面

    • 访问level3下的页面会报错

15.2、注销

  1. 添加注销按钮

    <a class="item" th:href="@{/logout}"><i class="sign-out icon"></i> 注销
    </a>
    
  2. 添加注销http.logout();

    //授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {//首页所有人可以访问,功能页只有对应有权限的人可以访问//请求授权的规则http.authorizeRequests().antMatchers("/").permitAll().antMatchers("/level1/**").hasRole("vip1").antMatchers("/level2/**").hasRole("vip2").antMatchers("/level3/**").hasRole("vip3");//没有权限会默认跳转到登录页面,需要开启登录的页面//http://localhost:8080/loginhttp.formLogin();//http://localhost:8080/logout//注销,开启了注销功能,跳转到首页http.logout().logoutSuccessUrl("/");
    }
    

    测试,登录之后点击注销按钮

15.3、Security与thymeleaf整合

security与thymeleaf整合,可以完成不同的人登录进入首页所看到的模块是不相同的

<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity4</artifactId><version>3.0.4.RELEASE</version>
</dependency>

权限控制,根据不同的角色显示不同的页面

  1. 导入sec命名空间

    <html lang="en" xmlns:th="http://www.thymeleaf.org"xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
    
  2. 在需要显示的地方加sec

    <!--登录注销-->
    <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="principal.authorities"></span></a><a class="item" th:href="@{/logout}"><i class="sign-out icon"></i> 注销</a></div>
    </div><!-- 动态菜单 -->
    <!-- 根据用户的角色动态的实现 -->
    <!-- 如果用户有vip1这个权限就显示,没有则隐藏 -->
    <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>
    

15.4、自定义登录页面

http.formLogin().loginPage("/toLogin");//请求跳转

controller

@RequestMapping("/toLogin")
public String toLogin() {return "/views/login";
}

15.5、记住我

开启记住我功能

//开启记住我功能,cookie,默认保存两周,自定义接收前端的参数
http.rememberMe().rememberMeParameter("remember");
<div class="field"><input type="checkbox" name="remember"/>记住我
</div>

再次打开浏览器,cookie信息依然存在。

总结

@EnableWebSecurity//开启WebSecurity模式,@Enablexxx,开启某个功能
public class SecurityConfig extends WebSecurityConfigurerAdapter {//授权@Overrideprotected void configure(HttpSecurity http) throws Exception {//首页所有人可以访问,功能页只有对应有权限的人可以访问//请求授权的规则http.authorizeRequests().antMatchers("/").permitAll().antMatchers("/level1/**").hasRole("vip1").antMatchers("/level2/**").hasRole("vip2").antMatchers("/level3/**").hasRole("vip3");//没有权限会默认跳转到登录页面,需要开启登录的页面//http://localhost:8080/loginhttp.formLogin().loginPage("/toLogin");//请求跳转//防止网站攻击http.csrf().disable();//关闭csrf功能,登陆失败可能存在的原因//注销,开启了注销功能,跳转到首页http.logout().logoutSuccessUrl("/");//开启记住我功能,cookie,默认保存两周,自定义接收前端的参数http.rememberMe().rememberMeParameter("remember");}//认证//报错:There is no PasswordEncoder mapped for the id "null"//在Spring Security 5.0+版本以上新增了很多的加密方法//new BCryptPasswordEncoder().encode("123456") 密码加密,防止反编译破解。@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("leishuai").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3").and().withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2").and().withUser("user").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");}
}

16、Shiro简介

  1. 什么是Shiro?

    • Apache Shiro是一个Java的安全(权限)框架
    • Shiro可以非常容易的开发出足够好的应用,其不仅可以在JavaSE环境,也可以用在JavaEE环境
    • Shiro可以完成认证、授权、加密,会话管理,Web集成,缓存等。
    • 下载地址:[http://shiro.apache.org/]
// 获取当前的用户对象Subject
Subject currentUser = SecurityUtils.getSubject();// 通过当前用户拿到Session
Session session = currentUser.getSession();// 当前用户是否被认证
currentUser.isAuthenticated()// 获得当前用户的认证
currentUser.getPrincipal()// 用户是否拥有xxx角色
currentUser.hasRole("schwartz")// 获得当前用户的一些权限,参数不同,效果不同
currentUser.isPermitted("lightsaber:wield")
currentUser.isPermitted("winnebago:drive:eagle5")

Shiro架构:Subject(用户),SecurityManager(管理所有用户),Realm(连接数据)

  1. 导入Shrio整合Spring的包

    <!-- Shiro整合Spring -->
    <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
    <dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.8.0</version>
    </dependency>
    
  2. 编写配置类

    // 自定义的Realm,只需要继承AuthorizingRealm
    public class UserRealm extends AuthorizingRealm {// 授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了授权" + principalCollection);return null;}// 认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {System.out.println("执行了认证" + authenticationToken);return null;}
    }
    

    ,,

    @Configuration
    public class ShiroConfig {// 第三步ShiroFilterFactoryBean@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();// 设置Shiro的安全管理器factoryBean.setSecurityManager(defaultWebSecurityManager);// 添加Shiro的内置过滤器/*anon:无需认证就可以访问authc:必须认证了才可以访问user:必须拥有“记住我”功能才能用perms:又有对某个资源的权限时才可访问role:拥有每个角色权限才可访问*/Map<String, String> filterMap = new LinkedHashMap<>();//filterMap.put("/user/add", "authc");//必须认证了才可以访问//filterMap.put("/user/update", "authc");filterMap.put("/user/*", "authc");factoryBean.setFilterChainDefinitionMap(filterMap);// 设置登录的请求factoryBean.setLoginUrl("/toLogin");return factoryBean;}// 第二步:DefaultWebSecurityManager@Beanpublic DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//关联UserRealmsecurityManager.setRealm(userRealm);return securityManager;}//自己写的类就被Spring容器托管,@UserRealm注册成组件// 第一步:创建Realm对象,需要自定义类@Beanpublic UserRealm userRealm() {return new UserRealm();}}
    
  3. controller

    @Controller
    public class IndexController {@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";}}
    
  4. 结果,如果用户在没有登陆的情况下点击添加或者修改,就会跳转到登录页面

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AMC8yDbP-1637377596135)(D:\Typora\img\image-20211105202953239.png)]

17、Shiro整合Mybatis

171、用户认证

准备工作:写一个根据用户name获取用户的方法,并测试:

@Autowired
UserService userService;@Test
void contextLoads() {User user = userService.queryUserByName("root");System.out.println(user);//User(id=5, name=root, pwd=123)}
@RequestMapping("/login")
public String login(String username, String password, Model model) {//获取当前的用户Subject subject = SecurityUtils.getSubject();// 封装用户的登录数据UsernamePasswordToken token = new UsernamePasswordToken(username, password);try {subject.login(token);return "index";} catch (UnknownAccountException e) {//用户名不存在model.addAttribute("msg", "用户名不存在");return "login";} catch (IncorrectCredentialsException e) {// 密码错误model.addAttribute("msg", "密码错误");return "login";}
}
@Autowired
private UserService userService;// 只要点击了登录就会进入这个方法
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("执行了认证" + token);UsernamePasswordToken userToken = (UsernamePasswordToken) token;// 从真实数据库中获取用户名、密码User user = userService.queryUserByName(userToken.getUsername());if (user == null) {return null;// 抛出异常 UnknownAccountException,用户名不存在}// 密码认证,Shiro做return new SimpleAuthenticationInfo("", user.getPwd(), "");
}
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/login}">用户名:<input type="text" name="username">密码:<input type="password" name="password"><input type="submit">
</form>

17.2、用户授权

// 添加Shiro的内置过滤器
/*anon:无需认证就可以访问authc:必须认证了才可以访问user:必须拥有“记住我”功能才能用perms:拥有对某个资源的权限时才可访问role:拥有每个角色权限才可访问*/
// 授权,正常的情况下,没有授权会跳到未授权的页面
// perms[user:add],user:add只有带了这个权限(user:add)的用户才可以访问
// 如果没有权限,就会(type=Unauthorized, status=401)
filterMap.put("/user/add","perms[user:add]");// 未授权页面,如果没有权限访问该页面,就会跳转
factoryBean.setUnauthorizedUrl("/noauth");

如果没有授权点击add就会跳转到未授权页面

@RequestMapping("/noauth")
@ResponseBody
public String noauth() {return "无权限访问该页面!";
}

接下来给用户授予add的权利,

// 给登入的用户授权,
info.addStringPermission("user:add");

但这是一个硬编码,每个用户登录都会走这个方法,所以要解决这个问题就要改造数据库,给数据库添加一个perms

User类也需要改

private String perms;

解决:

  1. 在认证中添加返回的user

    return new SimpleAuthenticationInfo(user, user.getPwd(), "");
    
  2. 给登录的用户授权

    // 拿到当前用户的登录对象
    Subject subject = SecurityUtils.getSubject();
    User currentUser = (User) subject.getPrincipal();//拿到user对象// 设置当前用户的权限
    info.addStringPermission(currentUser.getPerms());
    
  3. 在配置类中添加update权限

    filterMap.put("/user/add","perms[user:add]");
    filterMap.put("/user/update","perms[user:update]");
    
  4. 测试

    • 使用root登录(root用户只有update权限)

    • 点击添加

    • 点击修改

17.3、Shiro整合thymeleaf

用户登录进入只可以看到对应权限的按钮,没有权限的不进行展示。

  1. 导入Shiro整合thymeleaf包

    <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
    <dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.1.0</version>
    </dependency>
    
  2. 配置类

    // 整合ShiroDialect,用来整合thymeleaf和Shiro
    @Bean
    public ShiroDialect getShiroDialect() {return new ShiroDialect();
    }
    
  3. 将登陆后的user存放入session中

    Subject subject = SecurityUtils.getSubject();
    Session session = subject.getSession();
    session.setAttribute("loginUser",user);
    
  4. 前端

    <!-- 从session判断值 -->
    <div th:if="${session.loginUser == null}"><a th:href="@{/toLogin}">登录</a>
    </div><p th:text="${msg}"></p>
    <div shiro:hasPermission="user:add"><a th:href="@{/user/add}">添加</a>
    </div><div shiro:hasPermission="user:update"><a th:href="@{/user/update}">修改</a>
    </div>
    

Java学习-SpringBoot相关推荐

  1. Java学习——springboot整合mysql、redis、mongodb

    Java学习--springboot整合mysql.redis.mongodb 1. 环境搭建 2. 搭建目录结构 3. 各层内容 4. 测试 1. 环境搭建 创建SpringBoot工程时勾选如下技 ...

  2. springboot毕设项目java学习平台m55rv(java+VUE+Mybatis+Maven+Mysql)

    springboot毕设项目java学习平台m55rv(java+VUE+Mybatis+Maven+Mysql) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HB ...

  3. 物流快递系统前、后端+Java语言+SpringBoot项目+MVC三层架构+maven+Mysql+Tomcat+可以用于学习SpringBoot项目入门

    物流快递系统前.后端+Java语言+SpringBoot项目+MVC三层架构+Mysql+Tomcat+可以用于学习SpringBoot项目入门 可以用于课程设计.毕业设计的知识点入门学习 提示:此资 ...

  4. java B2B2C Springboot电子商务平台源码-Feign设计原理

    什么是Feign? 电子商务平台源码请加企鹅求求:一零三八七七四六二六.Feign 的英文表意为"假装,伪装,变形", 是一个http请求调用的轻量级框架,可以以Java接口注解的 ...

  5. java html提取_2020年全新Java学习路线,含配套资料,更易上手 - 打不过就跑吧

    新的一年来临,突如其来的疫情打破了平静的生活! 在家的你是否很无聊,如果无聊就来学习吧! 世上只有一种投资只赚不赔,那就是学习!!! 我在2020年升级了Java学习线路图,硬核升级,免费放送! 学完 ...

  6. Java学习计划,给自己一个学习线路

    动力节点Java培训最新上线Java实验班,等你来测试自己适不适合学习Java编程哦! Java学习计划,给自己一个学习线路.语言是学习成本比较低的,Java作为一种高级语言,其相关语法相对简单,各种 ...

  7. Java学习路线详解

    如今互联网行业发展火爆,各大企业对于[Java开发请添加链接描述](http://www.suzxms.com.cn/java/)人才的需求也在不断增加,很多刚毕业的同学找不到合适的工作,想学习一门赖 ...

  8. 从.Net到Java学习第四篇——spring boot+redis

    从.Net到Java学习系列目录 "学习java已经十天,有时也怀念当初.net的经典,让这语言将你我相连,怀念你......"接上一篇,本篇使用到的框架redis.FastJSO ...

  9. 基于java ssm springboot女士电商平台系统源码+文档设计

    作者主页:Java李杨勇 简介:Java领域优质创作者.[Java李杨勇]公号作者  简历模板.学习资料.面试题库.技术互助[关注我,都给你] 文末获取源码 主要技术:spring, springmv ...

最新文章

  1. PHP定时执行任务的实现
  2. UVA11825 Hackers' Crackdown
  3. DatePickerDialog 简单用法
  4. 为什么要用MyBatis-JDBC 连接数据库
  5. C++ unsigned char*转化为string的形式
  6. pythonturtle画彩虹蟒蛇_python如何用turtle库画蟒蛇?
  7. 【TI-ONE系列教程(一)】如何使用TI-ONE平台
  8. python怎么让图片旋转45度_是否有方法将matplotlib打印旋转45度?
  9. AXURE中SVG矢量图标的使用方法,以及图标颜色的改变方法
  10. java wlan_浅谈无线局域网WLAN
  11. 神通数据库导出sql脚本
  12. java中正则验证邮箱手机格式
  13. 关于导出聊天记录这件事……
  14. js二次压缩工具nodejs版
  15. 真正影响孩子一生的不是中高考,而是四、五年级!(深度好文)方格教育
  16. bongo cat mver手机版|键盘猫手机版
  17. 电影拍摄胶片比例_用于创建自己的电影胶片的10个很酷的Photoshop教程
  18. 第六十六章 Caché 函数大全 $TRANSLATE 函数
  19. 计算机系统基础:数据的表示和存储
  20. C++设计:关于CMatrix类的相关操作

热门文章

  1. t420i升级固态硬盘提升_小体积、大性能、台式机笔记本升级可选,酷兽M.2 SSD固态硬盘测评...
  2. 入门金融数据分析-Task1-数据了解and熟悉赛题
  3. 名悦集团:为什么说年底买车真的不划算
  4. java获取文件电脑路径
  5. elasticsearch 学习笔记3
  6. echarts中国地图飞线
  7. OpenPose 2019版总结
  8. 项目三 电梯控制器设计(FPGA综合应用设计)
  9. 如何改typecho主题头像_如何搭建hexo个人博客
  10. IIC、SPI、UART串行通信以及同步/异步通信学习