Based on best practices springboot web projects

springbootCan be said now do javawebto develop the hottest technology, I based springbootprocess to build a project, stepped on a lot of the pit, we found not only the integration framework introduced starterso simple.

To be simple, easy to use, more scalable, need to do a lot of secondary packaging, so they wrote based springbootweb projects of scaffolding, for some common integration framework, and a simple secondary package.

Project name baymaxis taken from the cartoon super marines inside the white, white is a medical inflatable robot, hope you like this project to light as dear, you can reduce your workload.

github https://github.com/zhaoguhong/baymax

web

web module is essential for a project to develop web module

maven dependence

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

For the separation of front and rear ends, recommendations directly @RestControllerannotations
should be noted that, not recommended to directly use RequstMapping annotation type and does not specify the method of writing , recommended GetMapingor PostMapingannotation like

@SpringBootApplication
@RestController
public class BaymaxApplication {

  public static void main(String[] args) {
    SpringApplication.run(BaymaxApplication.class, args);
  }

  @GetMapping("/test")
  public String test() {
    return "hello baymax";
  }
}

unit test

spring unit testing also provides good support

maven dependence

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

Adding @RunWith(SpringRunner.class)and @SpringBootTestcan be tested

@RunWith(SpringRunner.class)
@SpringBootTest
public class WebTest {
}

For Controllerthe interface layer, it can be directly used MockMvcfor testing

@RunWith(SpringRunner.class)
@SpringBootTest
public class WebTest {

  @Autowired
  private WebApplicationContext context;
  private MockMvc mvc;

  @Before
  public void setUp() throws Exception {
    mvc = MockMvcBuilders.webAppContextSetup(context).build();
  }

  @Test
  public void testValidation() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/test"))
        .andExpect(MockMvcResultMatchers.status().isOk())
        .andDo(MockMvcResultHandlers.print())
        .andExpect(MockMvcResultMatchers.content().string("hello baymax"));
  }

}

actuator Application Monitoring

spring actuator is provided monitoring application, the following configuration items common

# actuator端口 默认应用端口
management.server.port=8082
# 加载所有的端点 默认只加载 info,health
management.endpoints.web.exposure.include=*
# actuator路径前缀,默认 /actuator
management.endpoints.web.base-path=/actuator

chilli

lombok may generate a corresponding java code at compile the code look cleaner, while reducing development workload

Entity class after lombok

@Data
public class Demo {
  private Long id;
  private String userName;
  private Integer age;
}

It should be noted, @Datainclude @ToString、@Getter、@Setter、@EqualsAndHashCode、@RequiredArgsConstructor, RequiredArgsConstructor not a constructor with no arguments , no-argument structure annotation isNoArgsConstructor

RequiredArgsConstructor It generates a generated constant comprising a (final), and the construction method of identifying a variable @NotNull

baseEntity

The underlying field of the table pulled out a BaseEntity, all entity classes inherit the class

/**
 * 实体类基础类
 */
@Data
public abstract class BaseEntity implements Serializable {
  /**
   * 主键id
   */
  private Long id;
  /**
   * 创建人
   */
  private Long createdBy;
  /**
   * 创建时间
   */
  private Date createdTime;
  /**
   * 更新人
   */
  private Long updatedBy;
  /**
   * 更新时间
   */
  private Date updatedTime;
  /**
   * 是否删除
   */
  private Integer isDeleted;

}

The return value unified response

Front and rear ends are separated project substantially ajax call, it returns a unified package objects facilitate distal unitary

/**
 * 用于 ajax 请求的响应工具类
 */
@Data
public class ResponseResult<T> {
  // 未登录
  public static final String UN_LOGIN_CODE = "401";
  // 操作失败
  public static final String ERROR_CODE = "400";
  // 服务器内部执行错误
  public static final String UNKNOWN_ERROR_CODE = "500";
  // 操作成功
  public static final String SUCCESS_CODE = "200";
  // 响应信息
  private String msg;
  // 响应code
  private String code;
  // 操作成功,响应数据
  private T data;

  public ResponseResult(String code, String msg, T data) {
    this.msg = msg;
    this.code = code;
    this.data = data;
  }
}

Returned to the front end of the value ResponseResult-packaged

  /**
   * 测试成功的 ResponseResult
   */
  @GetMapping("/successResult")
  public ResponseResult<List<Demo>> test() {
    List<Demo> demos = demoMapper.getDemos();
    return ResponseResult.success(demos);
  }

  /**
   * 测试失败的 ResponseResult
   */
  @GetMapping("/errorResult")
  public ResponseResult<List<Demo>> demo() {
    return ResponseResult.error("操作失败");
  }

ResponseEntity

In fact, the encapsulation process ResponseEntity spring response, the status code contains ResponseEntity header information, response body of three parts

  /**
   * 测试请求成功
   * @return
   */
  @GetMapping("/responseEntity")
  public ResponseEntity<String> responseEntity() {
    return ResponseEntity.ok("请求成功");
  }

  /**
   * 测试服务器内部错误
   * @return
   */
  @GetMapping("/InternalServerError")
  public ResponseEntity<String> responseEntityerror() {
    return new ResponseEntity<>("出错了", HttpStatus.INTERNAL_SERVER_ERROR);
  }

abnormal

Custom exception system

For convenience exception handling, an exception defined system, BaymaxException as all custom exception parent

// 项目所有自定义异常的父类
public class BaymaxException extends RuntimeException
// 业务异常 该异常的信息会返回给用户
public class BusinessException  extends BaymaxException
// 用户未登录异常
public class NoneLoginException  extends BaymaxException

Global exception handler

Returned to the front-end processing, then all exceptions

@RestControllerAdvice
public class GlobalControllerExceptionHandler {

  /**
   * 业务异常
   */
  @ExceptionHandler(value = {BusinessException.class})
  public ResponseResult<?> handleBusinessException(BusinessException ex) {
    String msg = ex.getMessage();
    if (StringUtils.isBlank(msg)) {
      msg = "操作失败";
    }
    return ResponseResult.error(msg);
  }

  /**
   * 处理未登录异常
   */
  @ExceptionHandler(value = {NoneLoginException.class})
  public ResponseResult<?> handleNoneLoginException(NoneLoginException ex) {
    return ResponseResult.unLogin();
  }

Abnormal persistence

For an unknown anomaly, saved to the database, to facilitate subsequent troubleshooting

It should be noted that, if the project access to larger than recommended ELK this sophisticated log analysis systems, is not recommended to save the log to a relational database

  @Autowired
  private ExceptionLogMapper exceptionLogMapper;
  /**
   * 处理未知的错误
   */
  @ExceptionHandler(value = {Exception.class})
  public ResponseResult<Long> handleunknownException(Exception ex) {
    ExceptionLog log = new ExceptionLog(new Date(), ExceptionUtils.getStackTrace(ex));
    exceptionLogMapper.insert(log);
    ResponseResult<Long> result = ResponseResult.unknownError("服务器异常:" + log.getId());
    result.setData(log.getId());
    return result;
  }

Interface exception log

Foreign exception log to open a query interface /anon/exception/{异常日志id}, easy access

@RestController
@RequestMapping("/anon/exception")
public class ExceptionController {

  @Autowired
  private ExceptionLogMapper exceptionLogMapper;

  @GetMapping(value = "/{id}")
  public String getDemo(@PathVariable(value = "id") Long id) {
    return exceptionLogMapper.selectByPrimaryKey(id).getException();
  }

} 

Data validation

JSR 303 defines a set of Bean Validation specification, Hibernate Validator is to achieve Bean Validation and was extended

spring boot is also very easy to use

public class Demo extends BaseEntity{
  @NotBlank(message = "用户名不允许为空")
  private String userName;
  @NotBlank
  private String title;
  @NotNull
  private Integer age;
}

Notes can be added before the parameter @Valid

  @PostMapping("/add")
  public ResponseResult<String> add(@RequestBody @Valid Demo demo) {
    demoMapper.insert(demo);
    return ResponseResult.success();
  }

For each method alone can check the results of treatment, if not addressed, will throw an exception, an exception can be made to check the global process

In GlobalControllerExceptionHandleradd

  /**
   * 处理校验异常
   */
  @ExceptionHandler(MethodArgumentNotValidException.class)
  public ResponseResult<?> handleValidationException(MethodArgumentNotValidException ex) {
    BindingResult result = ex.getBindingResult();
    if (result.hasErrors()) {
      StringJoiner joiner = new StringJoiner(",");
      List<ObjectError> errors = result.getAllErrors();
      errors.forEach(error -> {
        FieldError fieldError = (FieldError) error;
        joiner.add(fieldError.getField() + " " + error.getDefaultMessage());
      });
      return ResponseResult.error(joiner.toString());
    } else {
      return ResponseResult.error("操作失败");
    }
  }

log

The default spring boot log is used logback, web logs are dependent module starter, so here no longer dependent on the introduction, detailed configuration

Change the log level

Actuator component provides a log-related interfaces, you can query the log level or dynamically change the log level

// 查看所有包/类的日志级别
/actuator/loggers
// 查看指定包/类日志级别 get 请求
/actuator/loggers/com.zhaoguhong.baymax.demo.controller.DemoController
//修改日志级别 post 请求 参数 {"configuredLevel":"debug"}
/actuator/loggers/com.zhaoguhong.baymax.demo.controller.DemoController

Logs cut

One log section, convenient method for performing recording of the parameters and the parameters

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogAspect {

  /**
   * 日志描述
   */
  String value() default "";

  /**
   * 日志级别
   */
  String level() default "INFO";

}

Directly added to the method to use

swagger

swagger document generation tool is a good use of

maven dependence

    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>2.7.0</version>
    </dependency>
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>2.7.0</version>
    </dependency>

Related

@Configuration
@EnableSwagger2
public class SwaggerConfig {

  @Value("${swagger.enable:false}")
  private boolean swaggerEnable;

  //文档访问前缀
  public static final String ACCESS_PREFIX = "/swagger-resources/**,/swagger-ui.html**,/webjars/**,/v2/**";

  @Bean
  public Docket docket() {
    return new Docket(DocumentationType.SWAGGER_2)
        .apiInfo(apiInfo())
        // 设置是否开启swagger,生产环境关闭
        .enable(swaggerEnable)
        .select()
        // 当前包路径
        .apis(RequestHandlerSelectors.basePackage("com.zhaoguhong.baymax"))
        .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
        .paths(PathSelectors.any())
        .build();
  }

  // 构建api文档的详细信息
  private ApiInfo apiInfo() {
    return new ApiInfoBuilder()
        // 页面标题
        .title("接口文档")
        // 创建人
        .contact(new Contact("孤鸿", "https://github.com/zhaoguhong/baymax", ""))
        // 版本号
        .version("1.0")
        // 描述
        .description("大白的接口文档")
        .build();
  }
}

The document plus a switch

#是否开启swagger文档,生产环境关闭
swagger.enable=true

Then you can have fun at the same time writing code written documentation of the

  @PostMapping("/add")
  @ApiOperation(value = "新增 demo")
  public ResponseResult<String> add(@RequestBody @Valid Demo demo) {
    demoMapper.insert(demo);
    return ResponseResult.success();
  }
@ApiModel("示例")
public class Demo extends BaseEntity{
  @ApiModelProperty("用户名")
  private String userName;
  @ApiModelProperty("标题")
  private String title;
  @ApiModelProperty("年龄")
  private Integer age;
}

Access localhost:8080/swagger-ui.htmlcan see the effect of

Database connection pool

springboot1.X database connection pool is tomcat connection pooling, springboot2 default database connection pool by the Tomcat replaced HikariCP , HikariCPis a high-performance JDBC connection pool, known as the fastest connection pool

Druid Alibaba database division produced, to monitor the health of the database connection pool, here selected Druidas a project database connection pool

maven dependence

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
      <version>1.1.10</version>
    </dependency>

Set Username Password

spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=123456

Then you can visit localhost:8080/druidto see the monitoring information

spring jdbc

maven dependence

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

jdbc spring to do encapsulation and abstraction, the most commonly used jdbcTemplateand NamedParameterJdbcTemplatetwo categories, the former with a placeholder, which uses named parameters, I jdbcDaomade one simple package, providing a unified external interface

jdbcDaoThe main methods are as follows:

// 占位符
find(String sql, Object... args)
// 占位符,手动指定映射mapper
find(String sql, Object[] args, RowMapper<T> rowMapper)
// 命名参数
find(String sql, Map<String, ?> paramMap)
// 命名参数,手动指定映射mapper
find(String sql, Map<String, ?> paramMap, RowMapper<T> rowMapper)
//springjdbc 原queryForMap方法,如果没查询到会抛异常,此处如果没有查询到,返回null
queryForMap(String sql, Object... args)
queryForMap(String sql, Map<String, ?> paramMap)
// 分页查询
find(Page<T> page, String sql, Map<String, ?> parameters, RowMapper<?> mapper)
// 分页查询
find(Page<T> page, String sql, RowMapper<T> mapper, Object... args)

jpa

jpaIs the javapersistence of standards, spring data jpaso that database operations more convenient, a description of spring data jpaitself is not achieved jpa, which is used by default providerishibernate

maven dependence

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

I put common method to extract out, and encapsulates a BaseRepository, when in use, the interface can be directly inherited

public interface DemoRepository extends BaseRepository<Demo> {

}

BaseRepository The main methods are as follows

// 新增,会对创建时间,创建人自动赋值
void saveEntity(T entity)
// 更新,会对更新时间,更新人自动赋值
void updateEntity(T entity)
// 逻辑删除
void deleteEntity(T entity)
// 批量保存
void saveEntites(Collection<T> entitys)
// 批量更新
void updateEntites(Collection<T> entitys)
// 批量逻辑删除
void deleteEntites(Collection<T> entitys)
// 根据id获取实体,会过滤掉逻辑删除的
T getById(Long id)

If you want to use sql traditional form, it can be used directly JpaDao, for convenience, I try to make JpaDao and JdbcDao interface to maintain unity

JpaDaoThe main methods are as follows

// 占位符 例如:from Demo where id =?
find(String sql, Object... args)
// 命名参数
find(String sql, Map<String, ?> paramMap)
// 分页
find(Page<T> page, String hql, Map<String, ?> parameters)
// 分页
find(Page<T> page, String hql, Object... parameters)

repeat

Redis excellent performance is key-value databases, often used for caching,
Java client has commonly used Jedisand Lettuce, spring data redisbased on Lettucedoing the second package

maven dependence

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

To read more convenient redis, change the serialization

@Configuration
public class RedisConfig {

  /**
   * 设置序列化方式
   */
  @Bean
  public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    redisTemplate.setKeySerializer(RedisSerializer.string());
    redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
    redisTemplate.setHashKeySerializer(RedisSerializer.string());
    redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
    return redisTemplate;
  }

  @Bean
  public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    // 将类名称序列化到json串中
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
        new Jackson2JsonRedisSerializer<Object>(Object.class);
    jackson2JsonRedisSerializer.setObjectMapper(om);
    return jackson2JsonRedisSerializer;
  }
}

spring cache

abstract spring cache a set of cache interface, by way of the use of annotations, can easily configure a specific implementation, detailed configuration

Redis used here as a cache provider, the default value serialization is JDK, for the convenience of viewing, can be modified to use json serialization

Sometimes set redis keyprefix demand, this is the default

    static CacheKeyPrefix simple() {
        // 在 cacheName 后面添加 "::"
        return name -> name + "::";
    }

There prefix property spring boot configuration provided

spring.cache.redis.key-prefix= # Key prefix.

But this is a pit, write the actual effect of this, will cacheNameget rid of, is clearly not what we want

CacheKeyPrefix cacheKeyPrefix = (cacheName) -> prefix;

What we want is to add the prefix in front, reservedcacheName

CacheKeyPrefix cacheKeyPrefix = (cacheName) -> keyPrefix + "::" + cacheName + "::";

Reference org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration, statementRedisCacheManager

@Configuration
@EnableConfigurationProperties(CacheProperties.class)
@EnableCaching
public class SpringCacheConfig {

  @Autowired
  private CacheProperties cacheProperties;

  @Bean
  public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    RedisCacheManagerBuilder builder = RedisCacheManager
        .builder(redisConnectionFactory)
        .cacheDefaults(determineConfiguration());
    List<String> cacheNames = this.cacheProperties.getCacheNames();
    if (!cacheNames.isEmpty()) {
      builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
    }
    return builder.build();
  }

  private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration() {
    Redis redisProperties = this.cacheProperties.getRedis();
    org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration
        .defaultCacheConfig();
    // 修改序列化为json
    config = config.serializeValuesWith(RedisSerializationContext.SerializationPair
        .fromSerializer(jackson2JsonRedisSerializer()));
    if (redisProperties.getTimeToLive() != null) {
      config = config.entryTtl(redisProperties.getTimeToLive());
    }
    if (redisProperties.getKeyPrefix() != null) {
      // 重写前缀拼接方式
      config = config.computePrefixWith((cacheName) -> redisProperties.getKeyPrefix() + "::" + cacheName + "::");
    }
    if (!redisProperties.isCacheNullValues()) {
      config = config.disableCachingNullValues();
    }
    if (!redisProperties.isUseKeyPrefix()) {
      config = config.disableKeyPrefix();
    }
    return config;
  }
    // 省略 jackson2JsonRedisSerializer() 

}

mogodb

MongoDB is a document database, using spring data mogodbeasy to operate mogodb

maven dependence

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

sprign data mogodbProvides MongoTemplatefor mogodb operate on the basis of class I and expands on it, you can customize your own method

@Configuration
public class MongoDbConfig {

  /**
   * 扩展自己的mogoTemplate
   */
  @Bean
  public MyMongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory,
      MongoConverter converter) {
    return new MyMongoTemplate(mongoDbFactory, converter);
  }

}

I extend a way paging, other methods can be extended depending on your situation

// mogodb 分页
public <T> Page<T> find(Page<T> page, Query query, Class<T> entityClass)

mybatis

maven dependence

    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.3.2</version>
    </dependency>

Common configured as follows

# 配置文件位置 classpath后面要加*,不然后面通配符不管用
mybatis.mapperLocations=classpath*:com/zhaoguhong/baymax/*/mapper/*Mapper.xml
# 开启驼峰命名自动映射
mybatis.configuration.map-underscore-to-camel-case=true

dao layer directly interfaces, simple, easy

@Mapper
public interface DemoMapper {
  /**
   * 注解方式
   */
  @Select("SELECT * FROM demo WHERE user_name = #{userName}")
  List<Demo> findByUserName(@Param("userName") String userName);
  /**
   * xml方式
   */
  List<Demo> getDemos();
}

It should be noted, the xml namespace must be a fully qualified name mapper class, so that it can establish a relationship of dao interface xml

<mapper namespace="com.zhaoguhong.baymax.demo.dao.DemoMapper">
  <select id="getDemos" resultType="com.zhaoguhong.baymax.demo.entity.Demo">
        select * from demo
    </select>
</mapper>

General mapper

mybatis single-table CRUD to write very much, General mapper good solution to this problem

maven dependence

    <dependency>
      <groupId>tk.mybatis</groupId>
      <artifactId>mapper-spring-boot-starter</artifactId>
      <version>1.2.4</version>
    </dependency>

Common configured as follows

# 通用mapper 多个接口时用逗号隔开
mapper.mappers=com.zhaoguhong.baymax.mybatis.MyMapper
mapper.not-empty=false
mapper.identity=MYSQL

Define their own MyMapperconvenience extension, MyMapperthe interface encapsulates common methods, and jpaof BaseRepositorythe like, not repeated here

Statement mapperneed to add Mapperannotations, but also somewhat cumbersome, you can use the scan mode

@Configuration
@tk.mybatis.spring.annotation.MapperScan(basePackages = "com.zhaoguhong.baymax.**.dao")
public class MybatisConfig {
}

The direct successor MyMapper interface can use

public interface DemoMapper extends MyMapper<Demo>{
}

Paging

pagehelper is paged plug mybatis of a good use of

maven dependence

    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper-spring-boot-starter</artifactId>
      <version>1.2.3</version>
    </dependency>

Common configured as follows

#pagehelper
#指定数据库类型
pagehelper.helperDialect=mysql
#分页合理化参数
pagehelper.reasonable=true

Use pagination

    PageHelper.startPage(1, 5);
    List<Demo> demos = demoMapper.selectAll();

pagehelper there are a lot of play, you can refer here

Custom Paging

pagehelper Although easy to use, but the project has its own page objects, so writing a single interceptor, integrate them together into
this place pay special attention to the plug-in order not to mistake

@Configuration
// 设置mapper扫描的包
@tk.mybatis.spring.annotation.MapperScan(basePackages = "com.zhaoguhong.baymax.**.dao")
@Slf4j
public class MybatisConfig {

  @Autowired
  private List<SqlSessionFactory> sqlSessionFactoryList;

  /**
   * 添加自定义的分页插件,pageHelper 的分页插件PageInterceptor是用@PostConstruct添加的,自定义的应该在其后面添加
   * 真正执行时顺序是反过来,先执行MyPageInterceptor,再执行 PageInterceptor
   *
   * 所以要保证 PageHelperAutoConfiguration 先执行
   */
  @Autowired
  public void addPageInterceptor(PageHelperAutoConfiguration pageHelperAutoConfiguration) {
    MyPageInterceptor interceptor = new MyPageInterceptor();
    for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
      sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
      log.info("注册自定义分页插件成功");
    }
  }

}

When using only need to pass self-defined objects can be paged

    Page<Demo> page = new Page<>(1, 10);
    demos = demoMapper.getDemos(page);

spring security

The security module is an indispensable part of the project, common security framework shiroand spring security, shiro relatively lightweight, very flexible, spring securityrelatively function better, but also seamless and spring. Here select spring securityas Security Framework

maven dependence

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

WebSecurityConfigurerAdapter class inheritance can be configured

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private SecurityProperties securityProperties;

  @Autowired
  private UserDetailsService userDetailsService;

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

    http
        .authorizeRequests()
        // 设置可以匿名访问的url
        .antMatchers(securityProperties.getAnonymousArray()).permitAll()
        // 其它所有请求都要认证
        .anyRequest().authenticated()
        .and()
        .formLogin()
        // 自定义登录页
        .loginPage(securityProperties.getLoginPage())
        // 自定义登录请求路径
        .loginProcessingUrl(securityProperties.getLoginProcessingUrl())
        .permitAll()
        .and()
        .logout()
        .permitAll();

    // 禁用CSRF
    http.csrf().disable();
  }


  @Override
  public void configure(WebSecurity web) throws Exception {
    String[] ignoringArray = securityProperties.getIgnoringArray();
    // 忽略的资源,直接跳过spring security权限校验
    if (ArrayUtils.isNotEmpty(ignoringArray)) {
      web.ignoring().antMatchers(ignoringArray);
    }
  }

  /**
   *
   * 声明密码加密方式
   */
  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth)
      throws Exception {
    auth.userDetailsService(userDetailsService)
        // 配置密码加密方式,也可以不指定,默认就是BCryptPasswordEncoder
        .passwordEncoder(passwordEncoder());
  }


}

Implement UserDetailsServicean interface, define your ownUserDetailsService

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

  @Autowired
  private UserRepository userRepository;

  @Override
  @Transactional
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userRepository.findByUsernameAndIsDeleted(username, SystemConstants.UN_DELETED);

    if (user == null) {
      throw new UsernameNotFoundException("username Not Found");
    }
    return user;
  }

}

Configuration Item

#匿名访问的url,多个用逗号分隔
security.anonymous=/test
#忽略的资源,直接跳过spring security权限校验,一般是用做静态资源,多个用逗号分隔
security.ignoring=/static/**,/images/**
#自定义登录页面
security.loginPage=/login.html
#自定义登录请求路径
security.loginProcessingUrl=/login

Project context

For the use, a context object encapsulating ContextHolder

// 获取当前线程HttpServletRequest
getRequest()
// 获取当前线程HttpServletResponse
getResponse()
// 获取当前HttpSession
getHttpSession()
setSessionAttribute(String key, Serializable entity)
getSessionAttribute(String key)
setRequestAttribute(String key, Object entity)
getRequestAttribute(String key)
// 获取 ApplicationContext
getApplicationContext()
//根据beanId获取spring bean
getBean(String beanId)
// 获取当前登录用户
getLoginUser()
// 获取当前登录用户 id
getLoginUserId()
// 获取当前登录用户 为空则抛出异常
getRequiredLoginUser()
// 获取当前登录用户id, 为空则抛出异常
getRequiredLoginUserId()

sign in

SSO (SSO, single sign-on) means, a plurality of systems share a set of system users, as long as one of the system log, the system does not need to re-login access to other

CASE

CAS (Central Authentication Service) is an open source project at Yale University, is the more popular single sign-on solution. In the CAS system, is responsible only for login is called the server, all other systems are referred to as client

Login process

  1. Users access the client, the client determines whether the login, if not logged in, redirect to login to the server
  2. Server login is successful, with ticket redirected to the client
  3. The client sends a request took the ticket to the server in exchange for user information, access to representation after a successful login

Logout Process

Jump to sso unified authentication center out, cas will notify all clients to logout

spring security integration cas

maven dependence

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

spring security for cas done a very good package, use of the process, only need to define the corresponding login and logout fifter fifter can integrate cas code I wrote in a WebSecurityConfigclass

Related property configuration

#是否开启单点登录
cas.enable = true
#服务端地址
cas.serverUrl=
#客户端地址
cas.clientUrl=
#登录地址
cas.loginUrl=${cas.serverUrl}/login
#服务端登出地址
cas.serverLogoutUrl=${cas.serverUrl}/logout
#单点登录成功回调地址
cas.clientCasUrl=${cas.clientUrl}/login/cas

mail

To use the template as freeMarker resolve, so it will depend on the introduction of freeMarker

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

    <dependency>
      <groupId>org.freemarker</groupId>
      <artifactId>freemarker</artifactId>
    </dependency>

Related

#邮件
#设置邮箱主机,163邮箱为smtp.163.com,qq为smtp.qq.com
spring.mail.host = smtp.163.com
spring.mail.username =
#授权码
spring.mail.password =
#默认的邮件发送人
mail.sender =

A package MailService

   // 根据相关配置发送邮件
  void sendMail(MailModel mailModel);
   // 发送简单的邮件
  void sendSimleMail(String to, String subject, String content);
   // 发送html格式的邮件
  void sendHtmlMail(String to, String subject, String content);
   // 发送带附件的邮件
  void sendAttachmentMail(String to, String subject, String content, String path);
   // 发送带附件的html格式邮件
  void sendAttachmentHtmlMail(String to, String subject, String content, String path);
   // 根据模版发送简单邮件
  void sendMailByTemplate(String to, String subject, String templateName,
      Map<String, Object> params);
  
}

maven

Mirroring

Set Ali cloud images, faster downloads
modify setting.xml, on the mirrors node, add

<mirror> 
    <id>alimaven</id> 
    <name>aliyun maven</name> 
    <url>http://maven.aliyun.com/nexus/content/groups/public/</url> 
    <mirrorOf>central</mirrorOf> 
</mirror> 

You can also add in the project pom.xml file, only the current project effective

  <repositories>
    <repository>
      <id>alimaven</id>
      <name>aliyun maven</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
      <releases>
        <enabled>true</enabled>
        <updatePolicy>daily</updatePolicy>
      </releases>
      <snapshots>
        <enabled>false</enabled>
        <checksumPolicy>warn</checksumPolicy>
      </snapshots>
      <layout>default</layout>
    </repository>
  </repositories>

to sum up

  1. follow the principle of spring boot out of the box, do not need to do too much, tutorials, online quality is uneven, and when 1.X and 2.X there are many different use, so the use of reference as far as possible the official document
  2. Sometimes the default configuration does not meet our needs, need to do some custom configuration, recommend a look at the springbootsource code automatically configured, do the customization process
  3. Technology is no silver bullet, when making technology selection Do not rely too much on a technology suited to their business technology is the best

Guess you like

Origin www.cnblogs.com/zhaoguhong/p/12008499.html