Welcome to 发呆鱼.|

发呆鱼

园龄:3年4个月粉丝:1关注:0

spring boot 入门学习笔记

1 简介

1.1 概述

​ 使用 Spring Boot 可以很容易地创建出能直接运行的独立的、生产级别的基于 Spring 的应用。我们对 Spring 平台和第三方类库有自己的考虑,因此您可以从最基本的开始。大多数 Spring Boot 应用只需要很少的 Spring 配置。您可以使用 Spring Boot 来创建一个可以使用 java -jar 命令来运行或者基于传统的 war 包部署的应用程序。我们还提供了一个用于运行 spring scripts 的命令行工具。

  • 为所有 Spring Boot 开发提供一个更快、更全面的入门体验。
  • 坚持自我虽好,但当需求出现偏离,您需要能迅速摆脱出来。
  • 提供大量非功能性特性相关项目(例如:内嵌服务器、安全、指标、健康检查、外部配置)。
  • 绝对没有代码生成,也不要求 XML 配置。

1.2 初步了解

pom.xml

pom.xml的父工程的父工程pring-boot-dependencies 是核心依赖。我们在引入一些依赖的时候不需要指定版本,正是因为有这些版本仓库。

启动器

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

启动器:springboot的启动场景。spring会将所有的应用场景变成一个个的启动器,需要什么功能,就只需要找到对应的启动器。Developing with Spring Boot

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

主程序

//@SpringBootApplication 标注这个类是一个springboot的应用 启动类下的所有资源被导入
@SpringBootApplication
public class SpringBoot01Application {

    public static void main(String[] args) {
        //将springboot 应用启动
        SpringApplication.run(SpringBoot01Application.class, args);
    }

}

@SpringBootConfiguration springboot的配置
    @Configuration spring配置类
    	@Component 说明这也是spring的一个组件
    
@EnableAutoConfiguration 自动配置
    @AutoConfigurationPackage 自动配置包
    	@Import({Registrar.class}) 自动配置 包注册
    @Import({AutoConfigurationImportSelector.class})  自动配置导入选择
    	
获取所有的配置
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
   

META-INF/spring.factories 自动配置的核心文件。

image-20220203000926141

自动装配


@Configuration(  //表示这是一个配置类
    proxyBeanMethods = false
)
//自动配置属性 ServerProperties
@EnableConfigurationProperties({ServerProperties.class})
//spring 底层注解 根据不同的条件,来判断当前配置或者类是否生效。
@ConditionalOnWebApplication(
    type = Type.SERVLET
)

@ConditionalOnClass({CharacterEncodingFilter.class})
@ConditionalOnProperty(
    prefix = "server.servlet.encoding",
    value = {"enabled"},
    matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
   
    

}

@Conditional的扩展注解

image-20220203153514443

一旦配置生效,这个配置类就会给容器中添加各种组件,这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的。

  • sping boot 启动会加载大量的自动配置类(spring.factories )

  • 看需要的功能有没有在springboot默认写好的自动配置中

  • 在看这个自动配置类中到底配置了那些组件

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

    xxxxAutoConfiguraryion 自动配置类 给容器添加组件

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

启动

SpringApplication类

  • 推断应用的类型是普通的项目还是web项目
  • 查找并加载所有可用初始化器。设置到initializers属性中
  • 找出所有应用程序监听器,设置到listeners属性中
  • 推断并设置main方法的定义类,找到并运行的主类

2 yaml 语法

springboot使用一个全局配置文件,配置文件名是固定的:application.xxx

application.properties key=value

application.yaml key: 空格 value

#普通键值对
name: ddd

#对象
student:
  name: aaa
  age: 12
  
#行内写法
student1: {name: ccc,age: 13}

#数组
pets:
  - cat
  - dog
pets1: [cat,dog]

松散绑定:

image-20220203144259440

JSR-303

@Validated  //数据校验
public class Student {

    @NonNull
    private String name;
    private int age;

image-20220203145216519

多环境配置

---
spring:
  profiles:
    active: dev
      
---
spring:
  profiles: dev
  
  
---
spring:
  profiles: test
    

3 springboot web开发

3.1 静态资源

//添加资源管理器
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    
    //如果静态资源属性已经被自定义, 故如果此处定义了,则下面的默认的staticLocations和 staticPathPattern就不会生效。
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled"); 
    } else {
        //注册,添加静态资源到 webjars/**
        this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
        //添加注册 静态资源即如下的的staticLocations和 staticPathPattern
        this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
            registration.addResourceLocations(this.resourceProperties.getStaticLocations());
            if (this.servletContext != null) {
                ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
                registration.addResourceLocations(new Resource[]{resource});
            }

        });
    }
}

静态资源配置

spring:
  mvc:
    static-path-pattern: /**

image-20220203174403260

image-20220203175731671

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
private String[] staticLocations;
//故 根据数组访问顺序 资源获取resources>static>public

3.2 首页定制

WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());




 private Resource getWelcomePage() {            
	Resource indexHtml = this.getIndexHtml(location);


private Resource getIndexHtml(Resource location) {
	Resource resource = location.createRelative("index.html");
// 会从静态资源处 找到第一个 index.html 

4 扩展springmvc

​ If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instances of those components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described in the Javadoc of @EnableWebMvc.

@Configuration
public class ExtendMVCConfig implements WebMvcConfigurer {
    // 实现了视图解析器接口的类,就可以看作为视图解析器
    @Bean
    public ViewResolver myViewResolver(){
        return new MyViewResolver();
    }


    //自定义一个视图解析器
    public static class MyViewResolver implements ViewResolver{
        @Override
        public View resolveViewName(String viewName, Locale locale) throws Exception {
            return null;
        }
    }

}

5 web开发

5.1 国际化

  1. 设置文件编码全部为UTF-8

image-20220204112143190

  1. 编写资源配置文件

image-20220204112210854

image-20220204121120722

  1. 自定义地域解析
public class MyLocalResolver implements LocaleResolver {

    //获取请求解析
    @Override
    public Locale resolveLocale(HttpServletRequest request) {

        //获取请求解析中的语言参数
        String la=request.getParameter("l");
        //拿到默认的,如果没有就使用默认的。
        Locale locale=Locale.getDefault();

        //如果请求的携带了国际化的参数
        if(!StringUtils.isEmpty(la)){
            String[] split=la.split("_");
            return  new Locale(split[0], split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}
//自定义国际化组件就生效了
@Bean
public LocaleResolver localResolver(){
    return new MyLocalResolver();
}

5.2 登录拦截

public class LoginHandlerInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {


        String requestURI = request.getRequestURI();

        String name= (String) request.getSession().getAttribute("name");
        if(name==null){
            request.setAttribute("msg","没有权限");
            request.getRequestDispatcher("/").forward(request,response);
            return false;
        }

        return true;
    }
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
            .excludePathPatterns("index.html","/","/user/login");
}

6 Data

6.1 连接mysql

依赖引入

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

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

数据库配置

datasource:
  username: root
  password: 123456
  driver-class-name: com.mysql.jdbc.Driver
  url: jdbc:mysql://localhost:3306/myemployees?useUnicode=utf-8&characterEncoding=utf-8

连接测试

@Autowired
DataSource dataSource;
@Test
void contextLoads() {
    try {
        System.out.println(dataSource.getConnection());
    } catch (SQLException e) {
        e.printStackTrace();
    }

}

打印出::HikariProxyConnection@631851861 wrapping com.mysql.cj.jdbc.ConnectionImpl@6adc5b9c

6.2 Druid

依赖

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

配置

datasource:
  username: root
  password: 123456
  driver-class-name: com.mysql.jdbc.Driver
  url: jdbc:mysql://localhost:3306/myemployees?useUnicode=utf-8&characterEncoding=utf-8
  type: com.alibaba.druid.pool.DruidDataSource

  #配置监控统计拦截器的fiters ,stat监控统计,wall:防御sql注入,log4j:日志记录
  filters: stat,wall,log4j
package com.fadaiyu.config;

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 org.springframework.data.repository.NoRepositoryBean;

import java.util.HashMap;

@Configuration
public class DruidDatasSource {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DruidDatasSource datasSource(){
        return new DruidDatasSource();
    }

    //后台监控
    @Bean
    public ServletRegistrationBean registrationBean(){
        ServletRegistrationBean<StatViewServlet> bean=new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");

        //后台需要有人登录,账号密码配置
        HashMap<String,String> initParameters=new HashMap<>();

        //增加配置
        initParameters.put("loginUsername","admin");
        initParameters.put("loginPassword","123456");

        //允许谁可以访问
        initParameters.put("allow","");

        //禁止谁能访问
        initParameters.put("xxx","xxx");

        //设置初始化参数
        bean.setInitParameters(initParameters);

        return bean;
    }

}

6.3 整合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.1</version>
</dependency>

配置

mybatis:
  type-aliases-package: com.fadaiyu.pojo
  mapper-locations: classpath:mapper/*.xml

使用

@Mapper
public interface JobsMapper {

    @Select("SELECT* FROM jobs")
    List<Jobs> queryJobList();

    Jobs queryJobsById(String jo_id);

}

<?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.fadaiyu.mapper.JobsMapper" >

    <select id="queryJobsById" resultType="Jobs">
        SELECT* FROM jobs WHERE job_id = #{job_id}
    </select>

</mapper>

7 安全

7.1 springsecurity

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

WebSecurityConfigurerAdapter 自定义security 策略

AuthenticationManagerBuilder 自定义认证策略

@EnableWebSecurity 开启WebSecurity模式

依赖

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


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

配置

@EnableWebSecurity  //aop 拦截器
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //首页所有人都可以访问 功能页只有对应有权限的人才能访问
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("//level1/*").hasRole("vip1");
        //没有登录权限,跳到登录页
        http.formLogin();

        //注销成功后返回首页
        http.logout().logoutSuccessUrl("/");
    }

    //认证
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("ding1").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");
    }
}

thymeleaf springsecurity5

<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>index</h1>
<!--
	sec:authorize :判断信息是否存在
	sec:authentication:取出相应的值
-->
<div sec:authorize="isAuthenticated()">
    <h1>已经登录</h1>

</div>
<a th:href="@{/level1/view1}">level1--view1</a>
<a th:href="@{/logout}">logout</a>
</body>
</html>

登录定制

//定制login页面,登录页面路径,登录请求路径,参数名
 // http.formLogin().loginPage("/toLogin").usernameParameter("userName").passwordParameter("passWodr");

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

7.2 shiro

7.2.1 简介

Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。

本教程只介绍基本的 Shiro 使用,不会过多分析源码等,重在使用。

Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。这不就是我们想要的嘛,而且 Shiro 的 API 也是非常简单;其基本功能点如下图所示:

img

  • Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份;
  • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
  • Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
  • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
  • Web Support:Web 支持,可以非常容易的集成到 Web 环境;
  • Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率;
  • Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
  • Testing:提供测试支持;
  • Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
  • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

记住一点,Shiro 不会去维护用户、维护权限;这些需要我们自己去设计 / 提供;然后通过相应的接口注入给 Shiro 即可。

接下来我们分别从外部和内部来看看 Shiro 的架构,对于一个好的框架,从外部来看应该具有非常简单易于使用的 API,且 API 契约明确;从内部来看的话,其应该有一个可扩展的架构,即非常容易插入用户自定义实现,因为任何框架都不能满足所有需求。

首先,我们从外部来看 Shiro 吧,即从应用程序角度的来观察如何使用 Shiro 完成工作。如下图:

img

可以看到:应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外 API 核心就是 Subject;其每个 API 的含义:

Subject:主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;

SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

Realm:域,Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。

也就是说对于我们而言,最简单的一个 Shiro 应用:

  1. 应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;
  2. 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。

从以上也可以看出,Shiro 不提供维护用户 / 权限,而是通过 Realm 让开发人员自己注入。

接下来我们来从 Shiro 内部来看下 Shiro 的架构,如下图所示:

img

  • Subject:主体,可以看到主体可以是任何可以与应用交互的 “用户”;
  • SecurityManager:相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。
  • Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
  • Authorizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
  • Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;
  • SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所以呢,Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);
  • SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把 Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能;
  • CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
  • Cryptography:密码模块,Shiro 提供了一些常见的加密组件用于如密码加密 / 解密的。

7.2.2 快速入门

引入依赖

<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.8.0</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.5.6</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>2.0.0-alpha6</version>
</dependency>

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

配置 和快速启动类

https://github.com/apache/shiro/tree/shiro-root-1.8.0/samples/quickstart/src (1.8)

7.2.3 与spring boot集成

依赖

<!-- 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>

配置

package com.fadaiyu.config.shiro;

import com.fadaiyu.mapper.UserMapper;
import com.fadaiyu.pojo.User;
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 org.springframework.beans.factory.annotation.Autowired;

public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserMapper userMapper;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权");

        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        info.addStringPermission("user:add");
        return info;

    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了认证");
        //用户名 密码  从数据库中取出

        UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;

        User user = userMapper.queryByName(token.getUsername());
        //用户名认证
        if(user==null){
            return null;  //抛出异常 UnknownAccountException
        }

        //密码认证 由shrio 做
        return new SimpleAuthenticationInfo("",user.getPassword(),"");

    }
}
package com.fadaiyu.config.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
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;
import java.util.Map;


@Configuration
public class ShiroConfig {


    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        factoryBean.setSecurityManager(defaultWebSecurityManager);

        //添加shiro 的内置过滤器
        /**
         * anon : 无需认真即可访问
         * authc: 必须认证才能访问
         * user: 必须拥有 记住我 功能才能访问
         * perms: 拥有对某个资源的权限才能访问
         * role: 拥有某个角色的权限才能访问
         */
        Map<String,String> map=new LinkedHashMap<>();

        //授权,未授权用户户 会跳转到未授权页面
        map.put("/level1/*","perms[user:add]");


        factoryBean.setFilterChainDefinitionMap(map);

        factoryBean.setLoginUrl("/toLogin");

        //设置未授权页面
        factoryBean.setUnauthorizedUrl("/unauthorized");

        return factoryBean;
    }


    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        //管理realm
        manager.setRealm(userRealm);

        return manager;

    }


    //创建realm对象
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }


    //整合shirodialect 用来整合shiro thymeleaf
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }
}

8 swagger

8.1 简介

​ Swagger 是一个规范且完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。

​ Swagger 的目标是对 REST API 定义一个标准且和语言无关的接口,可以让人和计算机拥有无须访问源码、文档或网络流量监测就可以发现和理解服务的能力。当通过 Swagger 进行正确定义,用户可以理解远程服务并使用最少实现逻辑与远程服务进行交互。与为底层编程所实现的接口类似,Swagger 消除了调用服务时可能会有的猜测。

8.2 使用

依赖引入

<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>3.0.0</version>
</dependency>


<!-- 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>

如果发生 documentxxxxxxx xxx错误,,添加如下配置:

spring:
    mvc:
      pathmatch:
        matching-strategy: ANT_PATH_MATCHER

配置

@Configuration
@EnableSwagger2 //开启swagger
public class SwaggerConfig {

    //配置swagger的bean实列
    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(this.apiInfo());
    }

    public ApiInfo apiInfo() {
        //作者信息
        Contact DEFAULT_CONTACT = new Contact("发呆鱼", "", "");

        return  new ApiInfo("api 文档",
                "啦啦啦",
                "1.0",
                "urn:tos",
                DEFAULT_CONTACT,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList());
    }
}

8.3 进一步配置

配置扫描接口

@Configuration
@EnableSwagger2 //开启swagger
public class SwaggerConfig {

    //可是设置多个分组
    @Bean
    public Docket docket1(){
        return new Docket(DocumentationType.SWAGGER_2).groupName("1");
    }

    //配置swagger的bean实列
    @Bean
    public Docket docket(Environment environment){

        //设置要显示的swagger 环境
        Profiles profiles=Profiles.of("dev");
        //通过environment.acceptsProfiles 判读是否处于自己设定的环境中
        boolean flag = environment.acceptsProfiles(profiles);


        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(this.apiInfo())
                .select()

                //RequestHandlerSelectors配置要扫描接口的方式
                //basePackage 指定要扫描的包
                //any 扫描全部
                // none 不扫描
                //withClassAnnotation 扫描类上的注解
                //withMethodAnnotation 扫描方法上的注解
                .apis(RequestHandlerSelectors.basePackage("com.fadaiyu.controller"))
                //.paths(PathSelectors.any()) 过滤什么路径
                .build()
                .groupName("A") //设置分组名
                .enable(flag);  //是否启动swagger
    }

    public ApiInfo apiInfo() {
        //作者信息
        Contact DEFAULT_CONTACT = new Contact("发呆鱼", "", "");

        return  new ApiInfo("api 文档",
                "啦啦啦",
                "1.0",
                "urn:tos",
                DEFAULT_CONTACT,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList());
    }
}

实体类配置

//只要返回的接口中,返回值中存在实体类,他就会被扫描到swagger中
@GetMapping("/a2")
public User userTest(){
    return new User();
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("用户实体类")
public class User {

    @ApiModelProperty("用户编号")
    private int id;
    private String name;
    private String password;
}

接口注释

package com.fadaiyu.controller;

import com.fadaiyu.pojo.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.annotation.Target;


@Api(tags = "hello 控制类")
@RestController
@RequestMapping("/hello")
public class HelloCon {

    @ApiOperation("接口a1")
    @PostMapping("/a1")
    public String test1(){
        return "aaa";
    }

    //只要返回的接口中,返回值中存在实体类,他就会被扫描到swagger中
    @GetMapping("/a2")
    public User userTest(){
        return new User();
    }

    @GetMapping("/a3")
    public String test2(@ApiParam("用户名") String username){
        return username;
    }
}

9 任务

9.1 异步任务

@SpringBootApplication
@EnableAsync //开启异步注解功能
public class SpringBoot01Application {

    public static void main(String[] args) {
        //将springboot 应用启动
        SpringApplication.run(SpringBoot01Application.class, args);
    }

}
//告诉spring 这是一个异步方法
@Async
public void hello(){

    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

9.2 发送邮件

依赖

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

配置

spring:
    mail:
      username: 邮箱名
      password: 密钥
      host: smtp.163.com

简单使用

@Test
public void mailTest(){
    //一个简单的邮件
    SimpleMailMessage simpleMessage=new SimpleMailMessage();

    simpleMessage.setSubject("发呆鱼");
    simpleMessage.setText("aaaaaaaa");
    simpleMessage.setTo("2305932120@qq.com");
    simpleMessage.setFrom("dingthefist@163.com");


    javaMailSender.send(simpleMessage);
}

@Test
public void mailTest2(){
    //一个复杂邮件
    MimeMessage mimeMessage=javaMailSender.createMimeMessage();

    //组装
    try {
        MimeMessageHelper mimeMessageHelper=new MimeMessageHelper(mimeMessage,true);

        //正文
        mimeMessageHelper.setSubject("复杂邮件");
        mimeMessageHelper.setText("<p stytle='color:red'> 啦啦啦啦啦啦啦</p>",true);

        //附件
        mimeMessageHelper.addAttachment("favicon.ico",new File("D:\\WorkeSpace_Study\\Java\\Spring_St\\Spring-Boot-01\\src\\main\\resources\\favicon.ico"));


        mimeMessageHelper.setTo("2305932120@qq.com");
        mimeMessageHelper.setFrom("dingthefist@163.com");


    } catch (MessagingException e) {
        e.printStackTrace();
    }


    javaMailSender.send(mimeMessage);
}

9.3 定时任务

TaskScheduler 任务调度者
TaskExecutor 任务执行者

@EnableScheduling 开启定时功能注解
@Scheduler 什么时候执行


    /**
     *  *:代表任何时间
     *  #, :代表连续时间
     * #-:代表持续范围
     * * /n :代表每隔多久执行一次
     * 第1* 一分钟的第多少秒
     * 第2*:一个小时当中的第几分钟
     * 第3*:一天中的第几个小时
     * 第4*:一个月当中的第几天
     * 第5*:一年之中第几个月
     * 第6*:一周当中的星期几 (0-7 0和7都是星期日)
     */
    //在一个特定时间执行
    @Scheduled(cron = "0 42 10 * * ?")
    public void schedulerTest1(){
        System.out.println("----------------------------------------------");
    }

10 整合redis

导入依赖

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

image-20220210205325819

在springboot 2.后,原来使用的jedis 被替换为lettuce

jedis :采用直连,多个线程操作的化,是不安全的。如果想要避免,使用jedis pool 连接池。bio

lettuce:采用netiy 实例可以再多个线程中共享,不存在线程不安全情况。 nio

@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }


	//提供了 redistemplate ,如果不需要,可以进行自定义	
    @Bean
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    	//默认的RedisTemplate 没有过多的设置, redis 对象都是需要序列化。
    	//两个泛型都是 object object 的类型,使用后需要转为string object
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean //
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }
}

配置

spring: 
   redis:
    port: 6379
    host: 127.0.0.1
  

测试

//操作不同的数据类型
//redisTemplate.opsForValue();// 操作字符串 类似 string
//redisTemplate.opsForList(); //操作list
//redisTemplate.opsForHash(); //操作哈希


// redisTemplate.opsForValue().set("name","fadaiyu");
//  System.out.println(redisTemplate.opsForValue().get("name"));

自定义redisTemplate

package com.fadaiyu.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    //编写自己的redisTemplate
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){

        RedisTemplate<String, Object> template=new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        //json 序列化方式
        Jackson2JsonRedisSerializer objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om=new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        objectJackson2JsonRedisSerializer.setObjectMapper(om);

        //string 序列化
        StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();

        //key采用string序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash 的key 采用string 序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //value 也采用string序列化方式
        template.setValueSerializer(stringRedisSerializer);
        //hash 的value 序列化采用jackson
        template.setHashValueSerializer(objectJackson2JsonRedisSerializer);

        template.afterPropertiesSet();
        return template;
    }

}

跟随《遇见狂神说》学习 未完待续



  1. Shiro 简介_w3cschool :https://www.w3cschool.cn/shiro/co4m1if2.html ↩︎

本文作者:发呆鱼

本文链接:https://www.cnblogs.com/dyiblog/articles/15927808.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   发呆鱼  阅读(59)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起