SpringBoot自定义配置

322 阅读4分钟

文件替换

在SpringBoot中,配置文件被自身的一系列强大的智能属性接管,但是其中的一些自动化配置不是我们想要的,或者距离目标有些许差距.这个时候就需要进行覆盖配置. 在使用Gradle进行项目管理的时候,如果我们在引入

org.springframework.boot:spring-boot-starter-web

它会引入一种处理json的jar包:

我们在项目中可能需要使用低版本的处理工具,或者其他的处理工具. 在Maven中使用标签将其中的

com.fasterxml.fastjson.core

给排除掉. 在Gradle中使用:

{
    exclude group:'com.fasterxml.jackson.core'
}
compile("...")

除了替换新的版本,也可以用来对项目进行瘦身或者对进行同类型工具的排除. 一般就是在Gradle或者Maven配置文件中进行排除,然后引入新的版本,按照正常的引入方式.

配置覆盖

使用SpringBoot自带的文件安全模块,结果总是差强人意. 因为你不但要忍受简陋的显示框,更要去日志中寻找密码.希望能自定义安全模块对其进行配置覆盖.

package com.pandy.readinglist.config;

import com.pandy.readinglist.dao.ReaderRepository;
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;

/**
 * @author Pandy
 * @version 1.0
 * @date 10:19
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private ReaderRepository readerRepository;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // "/"的请求只有经过身份验证才能且拥有Reader角色的用户才能访问
        http.authorizeRequests().antMatchers("/")
                .access("hasRole('READER')")//要求登录者有READER角色
                .antMatchers("/**").permitAll()//其他的所有访问请求路径对所有的用户开放了权限
                .and()
                .formLogin()
                .loginPage("/login")//设置登录表单的路径
                .failureUrl("/login?error=true");//同时将登录页与登录失败页指定到了/login
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(new UserDetailsService() {
                    @Override
                    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                        return (UserDetails) readerRepository.findById(username).get();
                    }
                });
    }
}

通过此配置,SpringBoot跳过了默认的自动配置,转而使用我们的配置. 通过实现WebSecurityConfigurerAdapter接口,覆盖两个方法,第一个方法configure(),"/"的请求只有经过身份验证并且拥有READER角色的用户才能访问.其他的所有请求路径对所有的用户放开了访问权限.这里还将登录页以及登录失败页指定到了/login.

package com.pandy.readinglist.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import java.io.Reader;

/**
 * @author Pandy
 * @version 1.0
 * @date 10:33
 */
public interface ReaderRepository extends JpaRepository<Reader,String> {
}

在这个应用程序中我们会用JPA用数据库来存储用户信息.第二个configure()方法设置了一个自定义的userDetailsService,这个服务是任意实现了userDetailsService的类,用于查找用户名的用户.

package com.pandy.readinglist.bean;

import lombok.Data;
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;

/**
 * @author Pandy
 * @version 1.0
 * @date 10:49
 */
@Data
@Entity
public class Reader implements UserDetails {

    @Id
    private String username;
    private String fullname;
    private String password;

    /**
     * 始终为用户授予READER权限
     * @return
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Arrays.asList(new SimpleGrantedAuthority("READER"));
    }

    /*@Override
    public String getPassword() {
        return null;
    }

    @Override
    public String getUsername() {
        return null;
    }*/

    /**
     * 不过期 不加锁 不禁用
     * @return
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

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

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

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

Reader实现了UserDetails接口以及其中的方法,这样Reader就能代表Spring Security里的用户了.getAuthorities()方法被覆盖过了.始终会为用户授予READER权限. 以及包括其中的是否过期,禁用,加锁.我们将其全部设置为true.表示不过期,不禁用,不加锁. 这个使用启动SpringBoot容器就能以读者的身份登录应用程序了.

通过属性文件外置配置

在application.yml中书写的自定义配置能够覆盖一些原有的默认配置,例如

server.port = 8000

将默认的服务器8080端口修改为8000; SpringBoot在启动的时候会检查200多项此类大大小小的配置.

logging:
  level:
    root: WARN
    org:
      springframework:
        security: DEBUG
  path: /var/logs
  file: BookWorm.log
spring:
  datasource:
    url: jdbc:mysql://localhost/readinglist
    username: root
    password: root
amazon:
  associateId: tinyfry520

当应用程序部署在不同的环境中时就需要配置不同的配置.Spring通过Profile注解来设置特定的环境的配置. 当需要在多重环境中进行切换的时候通过Profixe注解可以实现,也可以在配置文件中完成.

logging:
  level:
    root: WARN
    org:
      springframework:
        security: DEBUG
  path: /var/logs
  file: BookWorm.log
spring:
  datasource:
    url: jdbc:mysql://localhost/readinglist
    username: root
    password: root
amazon:
  associateId: tinyfry520
    # driver-class-name: com.mysql.jdbc.Driver

  # 在一个yml文件中可以配置多个不同环境中的配置文件
---
spring:
  profiles: development

logging:
  level:
    root: DEBUG
---
spring:
     profiles: production

logging:
  path: /tmp/
  file: BookWorm.log
  level:
    root: WARN

自定义错误页面

SpringBoot自动配置的默认错误处理器会查找名为error的视图,如果找不到就用默认的白标错误视图.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Oops</title>
    <link rel="stylesheet" th:href="@{/style.css}"></link>
</head>
<html>
    <div class="errorPage">
        <span class="oops">Oops!</span><br/>
        <img th:src="@{/MissingPage.jpg}"></img>
        <p>There seems to be a problem with the page you requested
            (<span th:text="${path}"></span>)</p>
        <p th:text="${'Details:' + message}"></p>
    </div>
</html>
</html>