《SpringBoot实战》笔记3

springBoot实战 第三章自定义配置

本章内容 
覆盖自动配置的Bean 
用外置属性进行配置 
自定义错误页

3.1 覆盖Spring Boot自动配置

一般来说,如果不用配置就能得到和显式配置一样的结果,那么不写配置是直接的选择。

既然如此,那干嘛还要多做额外的工作呢?如果不用编写和维护额外的配置代码也行,那何必还要它们呢? 大多数情况下,自动配置的Bean刚好能满足你的需要,不需要去覆盖它们。但某些情况下, Spring Boot在自动配置时还不能很好地进行推断。这里有个不错的例子:当你在应用程序里添加安全特性时,自动配置做得还不够好。

安全配置并不是放之四海而皆准的,围绕应用程序安全有很多决策要做,Spring Boot不能替你做决定。

虽然Spring Boot为安全提供了一些基本的自动配置,但是你还是需要自己覆盖一些配置以满足特 定的安全要求。

想知道如何用显式的配置来覆盖自动配置,我们先从为阅读列表应用程序添加Spring Security入手。

在了解自动配置提供了什么之后,我们再来覆盖基础的安全配置,以满足特定的 场景需求

3.1.1 保护应用程序
Spring Boot自动配置让应用程序的安全工作变得易如反掌,
你要做的只是添加Security起步 依赖。以Gradle为例,应添加如下依赖:
compile(“org.springframework.boot:spring-boot-starter-security”)

如果使用Maven,那么你要在项目的块中加入如下:

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

这样就搞定了!重新构建应用程序后运行即可,现在这就是一个安全的Web应用程序了!
Security起步依赖在应用程序的Classpath里添加了Spring Secuirty(和其他一些东西)。
Classpath里 有Spring Security后,自动配置就能介入其中创建一个基本的Spring Security配置。

试着在浏览器里打开该应用程序,你马上就会看到HTTP基础身份验证对话框。
此处的用户 名是user,密码就有点麻烦了。密码是在应用程序每次运行时随机生成后写入日志的,

你需要查 找日志消息(默认写入标准输出),找到此类内容:
Using default security password: d9d8abe5-42b5-4f20-a32a-76ee3df658d9

3.1.2 创建自定义的安全配置

package com.example.readinglist;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;

/**
 * 覆盖自动配置很简单,就当自动配置不存在,直接显式地写一段配置。
 * 这段显式配置的形式 不限,Spring支持的XML和Groovy形式配置都可以。
 * 在编写显式配置时,我们会专注于Java形式的配置。在Spring Security的场景下,
 * 这意味着写 一个扩展了WebSecurityConfigurerAdapter的配置类
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private ReaderRepository readerRepository;

    @Override
    protected  void configure(HttpSecurity http)throws Exception{
        http
                .authorizeRequests()
                .antMatchers("/").access("hasRole('READER')")
                .antMatchers("/**").permitAll()
                .and()
                .formLogin()
                .loginPage("/login")
                .failureUrl("/login?error=true");
    }

    @Override
    protected  void configure(AuthenticationManagerBuilder auth)throws Exception{
        auth
                .userDetailsService(new UserDetailsService() {
                    @Override
                    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                        //eturn readerRepository.getOne(username);
                        return readerRepository.getOne(username);
                    }
        });
    }
}
package com.example.readinglist;

import org.springframework.data.jpa.repository.JpaRepository;
import  java.util.List;

/**
 * 接下来,我们就要定义用于把Book对象持久化到数据库的仓库了。
 * ①因为用了Spring Data JPA, 所以我们要做的就是简单地定义一个接口,
 * 扩展一下Spring Data JPA的JpaRepository接口:
 */

/**
 * 通过扩展JpaRepository,ReadingListRepository直接继承了18个执行常用持久化操作 的方法。
 * JpaRepository是个泛型接口,有两个参数:仓库操作的领域对象类型,及其ID属性的 类型
 */
public interface ReadingListRepository extends JpaRepository<Book,Long> {
    /**
     * 可以根据读者的用户名来查找阅读列表。
     * @param reader
     * @return List
     */
    List<Book> findByReader(String reader);
}
package com.example.readinglist;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.List;

//这样组件扫描会自动将ReadingListController注册为 Spring应用程序上下文里的一个Bean
@Controller
//将其中所有的处理器 方法都映射到了“/”这个URL路径上。
@RequestMapping("/")
public class ReadingListController {
    private ReadingListRepository readingListRepository;

    public ReadingListController(ReadingListRepository readingListRepository){
        this.readingListRepository = readingListRepository;
    }

    /**
     * 处理/{reader}上的HTTP GET请求,根据路径里指定的读者,从(通 过控制器的构造器注入的)仓库获取Book列表。
     * 随后将这个列表塞入模型,用的键是 books,后返回readingList作为呈现模型的视图逻辑名称。
     * @param reader
     * @param model
     * @return String
     */
    @RequestMapping(value="/{reader}",method = RequestMethod.GET)
    public String readersBooks(
            @PathVariable("reader") String reader, Model model){
        List<Book> readingList = readingListRepository.findByReader(reader);
        if(readingList != null){
            model.addAttribute("books",readingList);
        }
        return "readingList";
    }

    /**
     *  addToReadingList():处理/{reader}上的HTTP POST请求,将请求正文里的数据绑定 到一个Book对象上。
     *  该方法把Book对象的reader属性设置为读者的姓名,
     *  随后通过仓 库的save()方法保存修改后的Book对象,
     *  后重定向到/{reader}(控制器中的另一个方 法会处理该请求)。
     * @param reader
     * @param book
     * @return
     */
    @RequestMapping(value = "/{reader}",method = RequestMethod.POST)
    public String addToReadingList(
            @PathVariable("reader") String reader,Book book){
        book.setReader(reader);
        readingListRepository.save(book);
        return "redirect:/{reader}";
    }
}
package com.example.readinglist;

import org.springframework.data.jpa.repository.JpaRepository;

//通过JPA持久化读者 Java Persistence API
public interface ReaderRepository extends JpaRepository<Reader,String> {
}

package com.example.readinglist;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Arrays;
import java.util.Collection;

//Reader用了@Entity注解,所以这是一个JPA实体
@Entity
//你应该还注意到Reader实现了UserDetails接口以及其中的方法,这样Reader就能代表 Spring Security里的用户了
public class Reader implements UserDetails {
    private static final long serialVersionUID = 1L;
//此外,它的username字段 上有@Id注解,表明这是实体的ID
    @Id
    private String username;
    private String fullname;
    private String password;

    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }

    public String getUsername() {
        return username;
    }
    public String getFullname() {
        return fullname;
    }

    public void setFullname(String fullname) {
        this.fullname = fullname;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    /**getAuthorities()方法被覆盖过了,始终会为用户授予READER 权限
     * 授予Reader权限
     * @return
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Arrays.asList(new SimpleGrantedAuthority("READER"));
    }

    //不过期,不加锁,不禁用
    //isAccountNonExpired()、 isAccountNonLocked()、isCredentialsNonExpired()
    // 和isEnabled()方法都返回true,这样读者账户就不会过期,不会被锁定,也不会被撤销。
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

/*
在一个大型应用程序里,赋予用户的授权本身也可能是实体,它们被 维护在独立的数据表里。
同样,表示一个账户是否为非过期、非锁定且可用的布尔值也 是数据库里的字段。
但是,出于演示考虑,我决定让这些细节保持简单,
以免分散我们 的注意力,影响正在讨论的话题——我说的是覆盖Spring Boot自动配置。
 */

/*
再重申一次,想要覆盖Spring Boot的自动配置,你所要做的仅仅是编写一个显式的配置。
 Spring Boot会发现你的配置,随后降低自动配置的优先级,以你的配置为准。
想弄明白这是如何 实现的,让我们揭开Spring Boot自动配置的神秘面纱,
看看它是如何运作的,以及它是怎么允许 自己被覆盖的。
 */
package com.example.readinglist;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.List;

//这样组件扫描会自动将ReadingListController注册为 Spring应用程序上下文里的一个Bean
@Controller
//将其中所有的处理器 方法都映射到了“/”这个URL路径上。
@RequestMapping("/")
public class ReadingListController {
    private ReadingListRepository readingListRepository;

    public ReadingListController(ReadingListRepository readingListRepository){
        this.readingListRepository = readingListRepository;
    }

    /**
     * 处理/{reader}上的HTTP GET请求,根据路径里指定的读者,从(通 过控制器的构造器注入的)仓库获取Book列表。
     * 随后将这个列表塞入模型,用的键是 books,后返回readingList作为呈现模型的视图逻辑名称。
     * @param reader
     * @param model
     * @return String
     */
    @RequestMapping(value="/{reader}",method = RequestMethod.GET)
    public String readersBooks(
            @PathVariable("reader") String reader, Model model){
        List<Book> readingList = readingListRepository.findByReader(reader);
        if(readingList != null){
            model.addAttribute("books",readingList);
        }
        return "readingList";
    }

    /**
     *  addToReadingList():处理/{reader}上的HTTP POST请求,将请求正文里的数据绑定 到一个Book对象上。
     *  该方法把Book对象的reader属性设置为读者的姓名,
     *  随后通过仓 库的save()方法保存修改后的Book对象,
     *  后重定向到/{reader}(控制器中的另一个方 法会处理该请求)。
     * @param reader
     * @param book
     * @return
     */
    @RequestMapping(value = "/{reader}",method = RequestMethod.POST)
    public String addToReadingList(
            @PathVariable("reader") String reader,Book book){
        book.setReader(reader);
        readingListRepository.save(book);
        return "redirect:/{reader}";
    }
}

在这里插入图片描述
在这里插入图片描述

3.1.3 掀开自动配置的神秘面纱

Spring Boot的DataSourceAutoConfiguration中定义的JdbcTemplate Bean就是一个非常简 单的例子,
演示了@ConditionalOnMissingBean如何工作:

@Bean
@ConditionalOnMissingBean(JdbcOperations.class)
 public JdbcTemplate jdbcTemplate() {   return new JdbcTemplate(this.dataSource); }

jdbcTemplate()方法上添加了@Bean注解,在需要时可以配置出一个JdbcTemplate Bean。
但它上面还加了@ConditionalOnMissingBean注解,
要求当前不存在JdbcOperations 类型(JdbcTemplate实现了该接口)的Bean时才生效。
如果当前已经有一个JdbcOperations Bean了,条件即不满足,不会执行jdbcTemplate()方法。
什么情况下会存在一个JdbcOperations Bean呢?Spring Boot的设计是加载应用级配置,随后再考虑自动配置类。
因此,如果你已经配置了一个JdbcTemplate Bean,
那么在执行自动配置 时就已经存在一个JdbcOperations类型的Bean了,于是忽略自动配置的JdbcTemplate Bean

关于Spring Security,自动配置会考虑几个配置类。
在这里讨论每个配置类的细节是不切实 际的,但覆盖Spring Boot自动配置的安全配置时,
重要的一个类是SpringBootWebSecurity- Configuration。以下是其中的一个代码片段:
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass({ EnableWebSecurity.class })
@ConditionalOnMissingBean(WebSecurityConfiguration.class)
@ConditionalOnWebApplication public class SpringBootWebSecurityConfiguration {

}

如你所见,SpringBootWebSecurityConfiguration上加了好几个注解。
看到@Condi- tionalOnClass注解后,你就应该知道Classpath里必须要有@EnableWebSecurity注解。
@ConditionalOnWebApplication 说明这必须是个Web 应用程序。
@ConditionalOn- MissingBean注解才是我们的安全配置类代替SpringBootWebSecurityConfiguration的关 键所在。

@ConditionalOnMissingBean注解要求当下没有WebSecurityConfiguration类型的 Bean。
虽然表面上我们并没有这么一个Bean,但通过在SecurityConfig上添加@EnableWeb-Security注解,
我们实际上间接创建了一个WebSecurityConfiguration Bean。所以在自动 配置时,这个Bean就已经存在了,
@ConditionalOnMissingBean条件不成立,SpringBoot- WebSecurityConfiguration提供的配置就被跳过了

虽然Spring Boot的自动配置和@ConditionalOnMissingBean让你能显式地覆盖那些可以 自动配置的Bean,但并不是每次都要做到这种程度。
让我们来看看怎么通过设置几个简单的配置 属性调整自动配置组件吧。

3.2 通过属性文件外置配置

待续…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
探索全栈前端技术的魅力:HTML+CSS+JS+JQ+Bootstrap网站源码深度解析 在这个数字化时代,构建一个既美观又功能强大的网站成为了许多开发者和企业追逐的目标。本份资源精心汇集了一套完整网站源码,融合了HTML的骨架搭建、CSS的视觉美化、JavaScript的交互逻辑、jQuery的高效操作以及Bootstrap的响应式设计,全方位揭秘了现代网页开发的精髓。 HTML,作为网页的基础,它构建了信息的框架;CSS则赋予网页生动的外观,让设计创意跃然屏上;JavaScript的加入,使网站拥有了灵动的交互体验;jQuery,作为JavaScript的强力辅助,简化了DOM操作与事件处理,让编码更为高效;而Bootstrap的融入,则确保了网站在不同设备上的完美呈现,响应式设计让访问无界限。 通过这份源码,你将: 学习如何高效组织HTML结构,提升页面加载速度与SEO友好度; 掌握CSS高级技巧,如Flexbox与Grid布局,打造适应各种屏幕的视觉盛宴; 理解JavaScript核心概念,动手实现动画、表单验证等动态效果; 利用jQuery插件快速增强用户体验,实现滑动效果、Ajax请求等; 深入Bootstrap框架,掌握移动优先的开发策略,响应式设计信手拈来。 无论是前端开发新手渴望系统学习,还是资深开发者寻求灵感与实用技巧,这份资源都是不可多得的宝藏。立即深入了解,开启你的全栈前端探索之旅,让每一个网页都成为技术与艺术的完美融合!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值