1. 程式人生 > >Spring Security技術棧開發企業級認證與授權(十一)開發簡訊驗證碼登入

Spring Security技術棧開發企業級認證與授權(十一)開發簡訊驗證碼登入

簡訊登入也是一種常見的登入方式,但是簡訊登入的方式並沒有整合到Spring Security中,所以往往還需要我們自己開發簡訊登入邏輯,將其整合到Spring Security中,使用Spring Security來進行校驗。本文將介紹開發簡訊登入的方法,並將其加入到Spring Security的驗證邏輯中。

一、簡訊登入邏輯設計以及圖片驗證碼程式碼重構

在前面一篇部落格《Spring Security技術棧開發企業級認證與授權(九)開發圖形驗證碼介面》中介紹瞭如何開發圖形驗證碼介面,並將驗證邏輯加入到Spring Security中,這裡將介紹如何開發簡訊驗證,兩者之間有許多非常類似的程式碼,所以在設計簡訊登入程式碼的時候,將它們進一步整合、抽象與重構。
圖形驗證碼和簡訊驗證碼重構後的結構圖如下所示:
這裡寫圖片描述

  • ValidateCodeController是這個驗證碼介面體系的入口,它主要抽象出可以同時接收兩種驗證碼的請求方式,使用請求型別type來進行區分。

  • ValidateCodeProcessor是一個介面,專門用來生成驗證碼,並將驗證碼存入到session中,最後將驗證碼傳送出去,傳送的方式有兩種,圖片驗證碼是寫回到response中,簡訊驗證碼呼叫第三方簡訊服務平臺的API進行傳送,比如阿里巴巴的簡訊服務。

  • AbstractValidateCodeProcessor是一個抽象類,它實現了ValidateCodeProcessor介面,並提供了抽象方法send方法,因為圖片的傳送方法和簡訊的傳送方法具體實現不同,所以得使用具體的方法進行傳送。這裡面的create

    方法完成了驗證碼的生成、儲存與傳送功能。

  • ValidateCodeGenerator也是一個介面,它有兩個實現類,分別是ImageCodeGeneratorSmsCodeGenerator,它們具體是完成了程式碼的生成邏輯。

  • ImageCodeProcessor和SmsCodeProcessor是專門用來重寫send方法的一個處理器,展示了兩種驗證碼的不同傳送方式。

1)將簡訊驗證碼和圖形驗證碼的相同屬性進行抽取

簡訊驗證碼和圖形驗證後包含屬性有codeexpireTime,簡訊驗證碼只有這兩個屬性,而圖形驗證碼還多一個BufferedImage例項物件屬性,所以將共同屬性進行抽取,抽取為ValidateCode

類,程式碼如下:

package com.lemon.security.core.validate.code;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.time.LocalDateTime;

/**
 * @author lemon
 * @date 2018/4/17 下午8:13
 */
@Data
@AllArgsConstructor
public class ValidateCode {

    private String code;

    private LocalDateTime expireTime;

    public boolean isExpired() {
        return LocalDateTime.now().isAfter(expireTime);
    }
}

抽取後的圖片驗證碼實體類為:

package com.lemon.security.core.validate.code.image;

import com.lemon.security.core.validate.code.ValidateCode;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.awt.image.BufferedImage;
import java.time.LocalDateTime;

/**
 * 圖片驗證碼實體類
 *
 * @author lemon
 * @date 2018/4/6 下午4:34
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class ImageCode extends ValidateCode {

    private BufferedImage image;

    public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
        super(code, expireTime);
        this.image = image;
    }

    public ImageCode(BufferedImage image, String code, int expireIn) {
        super(code, LocalDateTime.now().plusSeconds(expireIn));
        this.image = image;
    }
}

圖片驗證碼實體類繼承了ValidateCode類,那麼在寫一個簡訊驗證碼實體類:

package com.lemon.security.core.validate.code.sms;

import com.lemon.security.core.validate.code.ValidateCode;

import java.time.LocalDateTime;

/**
 * 簡訊驗證碼實體類
 *
 * @author lemon
 * @date 2018/4/17 下午8:18
 */
public class SmsCode extends ValidateCode {

    public SmsCode(String code, LocalDateTime expireTime) {
        super(code, expireTime);
    }

    public SmsCode(String code, int expireIn) {
        super(code, LocalDateTime.now().plusSeconds(expireIn));
    }
}

簡訊驗證碼只需要繼承ValidateCode即可,沒有其他多餘的屬性增加。
對於配置的程式碼,也是可以進一步進行重構,簡訊驗證碼和圖片驗證碼在配置上有幾個重複的屬性,比如:驗證碼長度length,驗證碼過期時間expireIn,以及需要新增簡訊驗證的url地址。ImageCodePropertiesSmsCodeProperties共同抽取出CodeProperties,程式碼如下:

  • CodeProperties
package com.lemon.security.core.properties;

import lombok.Data;

/**
 * @author lemon
 * @date 2018/4/17 下午9:11
 */
@Data
public class CodeProperties {

    /**
     * 驗證碼長度
     */
    private int length = 6;
    /**
     * 驗證碼過期時間
     */
    private int expireIn = 60;

    /**
     * 需要驗證碼的url字串,用英文逗號隔開
     */
    private String url;
}
  • ImageCodeProperties
package com.lemon.security.core.properties;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 圖形驗證碼的預設配置
 *
 * @author lemon
 * @date 2018/4/6 下午9:42
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class ImageCodeProperties extends CodeProperties {

    public ImageCodeProperties() {
        setLength(4);
    }

    /**
     * 驗證碼寬度
     */
    private int width = 67;
    /**
     * 驗證碼高度
     */
    private int height = 23;
}
  • SmsCodeProperties
package com.lemon.security.core.properties;

/**
 * @author lemon
 * @date 2018/4/17 下午9:13
 */
public class SmsCodeProperties extends CodeProperties {

}

為了實現配置資訊可以由使用者自定義配置,還需要將其加入到讀取配置檔案的配置類中,建立一個ValidateCodeProperties類,將圖片驗證碼和簡訊驗證碼例項物件作為屬性配置進去,程式碼如下:

package com.lemon.security.core.properties;

import lombok.Data;

/**
 * 封裝多個配置的類
 *
 * @author lemon
 * @date 2018/4/6 下午9:45
 */
@Data
public class ValidateCodeProperties {

    private ImageCodeProperties image = new ImageCodeProperties();
    private SmsCodeProperties sms = new SmsCodeProperties();

}

再將ValidateCodeProperties封裝到整個安全配置類SecurityProperties中,具體的程式碼如下:

package com.lemon.security.core.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * @author lemon
 * @date 2018/4/5 下午3:08
 */
@Data
@ConfigurationProperties(prefix = "com.lemon.security")
public class SecurityProperties {

    private BrowserProperties browser = new BrowserProperties();

    private ValidateCodeProperties code = new ValidateCodeProperties();
}

這個時候就可以讀取到使用者自定義的配置檔案application.properties或者application.yml中的配置。關於驗證碼的配置方式的application.properties檔案內容形式如下,application.yml類似:

com.lemon.security.code.image.length=4
com.lemon.security.code.sms.length=6

2)編寫ValidateCodeProcessor介面
ValidateCodeProcessor介面主要是完成了驗證碼的生成、儲存與傳送的一整套流程,介面的主要設計如下所示:

package com.lemon.security.core.validate.code;

import org.springframework.web.context.request.ServletWebRequest;

import javax.servlet.http.HttpServletRequest;

/**
 * 驗證碼生成介面
 *
 * @author lemon
 * @date 2018/4/17 下午9:46
 */
public interface ValidateCodeProcessor {

    String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_";
    String CODE_PROCESSOR = "CodeProcessor";

    /**
     * 生成驗證碼
     *
     * @param request 封裝了 {@link HttpServletRequest} 例項物件的請求
     * @throws Exception 異常
     */
    void create(ServletWebRequest request) throws Exception;
}

由於圖片驗證碼和簡訊驗證碼的生成和儲存、傳送等流程是固定的,只是在生成兩種驗證碼的時候分別呼叫各自的生成方法,儲存到session中是完全一致的,最後的傳送各有不同,圖片驗證碼是寫到response中,而簡訊驗證碼是呼叫第三方簡訊傳送平臺的SDK來實現傳送功能。所以這裡寫一個抽象類來實現ValidateCodeProcessor介面。

package com.lemon.security.core.validate.code.impl;

import com.lemon.security.core.validate.code.ValidateCodeGenerator;
import com.lemon.security.core.validate.code.ValidateCodeProcessor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletWebRequest;

import java.util.Map;

/**
 * @author lemon
 * @date 2018/4/17 下午9:56
 */
@Component
public abstract class AbstractValidateCodeProcessor<C> implements ValidateCodeProcessor {

    private static final String SEPARATOR = "/code/";

    /**
     * 操作session的工具集
     */
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    /**
     * 這是Spring的一個特性,就是在專案啟動的時候會自動收集系統中 {@link ValidateCodeGenerator} 介面的實現類物件
     */
    @Autowired
    private Map<String, ValidateCodeGenerator> validateCodeGeneratorMap;

    @Override
    public void create(ServletWebRequest request) throws Exception {
        C validateCode = generate(request);
        save(request, validateCode);
        send(request, validateCode);
    }

    /**
     * 生成驗證碼
     *
     * @param request ServletWebRequest例項物件
     * @return 驗證碼例項物件
     */
    @SuppressWarnings("unchecked")
    private C generate(ServletWebRequest request) {
        String type = getProcessorType(request);
        ValidateCodeGenerator validateCodeGenerator = validateCodeGeneratorMap.get(type.concat(ValidateCodeGenerator.CODE_GENERATOR));
        return (C) validateCodeGenerator.generate(request);
    }

    /**
     * 儲存驗證碼到session中
     *
     * @param request      ServletWebRequest例項物件
     * @param validateCode 驗證碼
     */
    private void save(ServletWebRequest request, C validateCode) {
        sessionStrategy.setAttribute(request, SESSION_KEY_PREFIX.concat(getProcessorType(request).toUpperCase()), validateCode);
    }

    /**
     * 傳送驗證碼
     *
     * @param request      ServletWebRequest例項物件
     * @param validateCode 驗證碼
     * @throws Exception 異常
     */
    protected abstract void send(ServletWebRequest request, C validateCode) throws Exception;

    /**
     * 獲取請求URL中具體請求的驗證碼型別
     *
     * @param request ServletWebRequest例項物件
     * @return 驗證碼型別
     */
    private String getProcessorType(ServletWebRequest request) {
        // 獲取URI分割後的第二個片段
        return StringUtils.substringAfter(request.getRequest().getRequestURI(), SEPARATOR);
    }
}

對上面的程式碼進行解釋:

  • 首先將驗證碼生成介面ValidateCodeGenerator的實現類物件注入到Map集合中,這個是Spring的一個特性。

  • 抽象類中實現了ValidateCodeProcessor介面的create方法,從程式碼中可以看出,它主要是完成了驗證碼的建立、儲存和傳送的功能。

  • generate方法根據傳入的不同泛型而生成了特定的驗證碼,而泛型的傳入是通過AbstractValidateCodeProcessor的子類來實現的。

  • save方法是將生成的驗證碼例項物件存入到session中,兩種驗證碼的儲存方式一致,所以程式碼也是通用的。

  • send方法一個抽象方法,分別由ImageCodeProcessorSmsCodeProcessor來具體實現,也是根據泛型來判斷具體呼叫哪一個具體的實現類的send方法。

3)編寫驗證碼的生成介面

package com.lemon.security.core.validate.code;

import org.springframework.web.context.request.ServletWebRequest;

/**
 * @author lemon
 * @date 2018/4/7 上午11:06
 */
public interface ValidateCodeGenerator {

    String CODE_GENERATOR = "CodeGenerator";

    /**
     * 生成圖片驗證碼
     *
     * @param request 請求
     * @return ImageCode例項物件
     */
    ValidateCode generate(ServletWebRequest request);
}

它有兩個具體的實現,分別是ImageCodeGeneratorSmsCodeGenerator,具體程式碼如下:

package com.lemon.security.core.validate.code.image;

import com.lemon.security.core.properties.SecurityProperties;
import com.lemon.security.core.validate.code.ValidateCodeGenerator;
import lombok.Data;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;

/**
 * 圖片驗證碼生成器
 *
 * @author lemon
 * @date 2018/4/7 上午11:09
 */
@Data
public class ImageCodeGenerator implements ValidateCodeGenerator {

    private static final String IMAGE_WIDTH_NAME = "width";
    private static final String IMAGE_HEIGHT_NAME = "height";
    private static final Integer MAX_COLOR_VALUE = 255;

    private SecurityProperties securityProperties;

    @Override
    public ImageCode generate(ServletWebRequest request) {
        int width = ServletRequestUtils.getIntParameter(request.getRequest(), IMAGE_WIDTH_NAME, securityProperties.getCode().getImage().getWidth());
        int height = ServletRequestUtils.getIntParameter(request.getRequest(), IMAGE_HEIGHT_NAME, securityProperties.getCode().getImage().getHeight());
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();

        Random random = new Random();

        // 生成畫布
        g.setColor(getRandColor(200, 250));
        g.fillRect(0, 0, width, height);
        g.setFont(new Font("Times New Roman", Font.ITALIC, 20));
        g.setColor(getRandColor(160, 200));
        for (int i = 0; i < 155; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int xl = random.nextInt(12);
            int yl = random.nextInt(12);
            g.drawLine(x, y, x + xl, y + yl);
        }

        // 生成數字驗證碼
        StringBuilder sRand = new StringBuilder();
        for (int i = 0; i < securityProperties.getCode().getImage().getLength(); i++) {
            String rand = String.valueOf(random.nextInt(10));
            sRand.append(rand);
            g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
            g.drawString(rand, 13 * i + 6, 16);
        }

        g.dispose();

        return new ImageCode(image, sRand.toString(), securityProperties.getCode().getImage().getExpireIn());
    }

    /**
     * 生成隨機背景條紋
     *
     * @param fc 前景色
     * @param bc 背景色
     * @return RGB顏色
     */
    private Color getRandColor(int fc, int bc) {
        Random random = new Random();
        if (fc > MAX_COLOR_VALUE) {
            fc = MAX_COLOR_VALUE;
        }
        if (bc > MAX_COLOR_VALUE) {
            bc = MAX_COLOR_VALUE;
        }
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }
}
package com.lemon.security.core.validate.code.sms;

import com.lemon.security.core.properties.SecurityProperties;
import com.lemon.security.core.validate.code.ValidateCodeGenerator;
import lombok.Data;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletWebRequest;

/**
 * 簡訊驗證碼生成器
 *
 * @author lemon
 * @date 2018/4/7 上午11:09
 */
@Data
@Component("smsCodeGenerator")
public class SmsCodeGenerator implements ValidateCodeGenerator {

    private final SecurityProperties securityProperties;

    @Autowired
    public SmsCodeGenerator(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }

    @Override
    public SmsCode generate(ServletWebRequest request) {
        String code = RandomStringUtils.randomNumeric(securityProperties.getCode().getSms().getLength());
        return new SmsCode(code, securityProperties.getCode().getSms().getExpireIn());
    }
}

兩個實現類完成了具體的驗證碼生成邏輯,根據傳入的泛型然後進行強轉之後便可呼叫各自的生成邏輯方法。

4)編寫驗證碼的傳送邏輯類
不同的驗證碼的傳送邏輯是不一樣的,圖片驗證碼是寫回response中,而簡訊驗證碼是將驗證碼傳送到指定手機號的手機上。
圖片驗證碼的傳送邏輯類的程式碼如下:

package com.lemon.security.core.validate.code.image;

import com.lemon.security.core.validate.code.impl.AbstractValidateCodeProcessor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletWebRequest;

import javax.imageio.ImageIO;

/**
 * @author lemon
 * @date 2018/4/17 下午11:37
 */
@Component("imageCodeProcessor")
public class ImageCodeProcessor extends AbstractValidateCodeProcessor<ImageCode> {

    private static final String FORMAT_NAME = "JPEG";

    /**
     * 傳送圖形驗證碼,將其寫到相應中
     *
     * @param request   ServletWebRequest例項物件
     * @param imageCode 驗證碼
     * @throws Exception 異常
     */
    @Override
    protected void send(ServletWebRequest request, ImageCode imageCode) throws Exception {
        ImageIO.write(imageCode.getImage(), FORMAT_NAME, request.getResponse().getOutputStream());
    }
}

簡訊驗證碼的傳送邏輯類的程式碼如下:

package com.lemon.security.core.validate.code.sms;

import com.lemon.security.core.validate.code.impl.AbstractValidateCodeProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;

/**
 * @author lemon
 * @date 2018/4/17 下午11:41
 */
@Component("smsCodeProcessor")
public class SmsCodeProcessor extends AbstractValidateCodeProcessor<SmsCode> {

    private static final String SMS_CODE_PARAM_NAME = "mobile";

    private final SmsCodeSender smsCodeSender;

    @Autowired
    public SmsCodeProcessor(SmsCodeSender smsCodeSender) {
        this.smsCodeSender = smsCodeSender;
    }

    @Override
    protected void send(ServletWebRequest request, SmsCode smsCode) throws Exception {
        String mobile = ServletRequestUtils.getRequiredStringParameter(request.getRequest(), SMS_CODE_PARAM_NAME);
        smsCodeSender.send(mobile, smsCode.getCode());
    }
}

注意到上面的簡訊傳送呼叫了SmsCodeSender的實現類,因此和圖片的傳送有所區別。而在設計中,SmsCodeSender有一個預設的實現,也就是自帶的簡訊傳送方式,但是在實際的開發過程中,往往需要開發者覆蓋自帶的傳送邏輯,而是採用自定義的傳送邏輯,所以需要預設的簡訊傳送方式是可以被覆蓋的。SmsCodeSender介面程式碼如下:

package com.lemon.security.core.validate.code.sms;

/**
 * 簡訊驗證傳送介面
 *
 * @author lemon
 * @date 2018/4/17 下午8:25
 */
public interface SmsCodeSender {

    /**
     * 簡訊驗證碼傳送介面
     *
     * @param mobile 手機號
     * @param code   驗證碼
     */
    void send(String mobile, String code);
}

它的預設實現類程式碼啊如下:

package com.lemon.security.core.validate.code.sms;

/**
 * 預設的簡訊傳送邏輯
 *
 * @author lemon
 * @date 2018/4/17 下午8:26
 */
public class DefaultSmsCodeSender implements SmsCodeSender {

    @Override
    public void send(String mobile, String code) {
        // 這裡僅僅寫個列印,具體邏輯一般都是呼叫第三方介面傳送簡訊
        System.out.println("向手機號為:" + mobile + "的使用者傳送驗證碼:" + code);
    }
}

注意到上面的程式碼並沒有使用@Component註解來標註為一個SpringBean,這麼做不是說它不由Spring管理,而是需要配置的可以被覆蓋的形式,所以在ValidateCodeBeanConfig類中加上配置其為Spring Bean的程式碼,為了體現程式碼的完整性,這裡貼出ValidateCodeBeanConfig類中的所有程式碼。

package com.lemon.security.core.validate.code;

import com.lemon.security.core.properties.SecurityProperties;
import com.lemon.security.core.validate.code.image.ImageCodeGenerator;
import com.lemon.security.core.validate.code.sms.DefaultSmsCodeSender;
import com.lemon.security.core.validate.code.sms.SmsCodeSender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author lemon
 * @date 2018/4/7 上午11:22
 */
@Configuration
public class ValidateCodeBeanConfig {

    private final SecurityProperties securityProperties;

    @Autowired
    public ValidateCodeBeanConfig(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }

    @Bean
    @ConditionalOnMissingBean(name = "imageCodeGenerator")
    public ValidateCodeGenerator imageCodeGenerator() {
        ImageCodeGenerator imageCodeGenerator = new ImageCodeGenerator();
        imageCodeGenerator.setSecurityProperties(securityProperties);
        return imageCodeGenerator;
    }

    @Bean
    @ConditionalOnMissingBean(SmsCodeSender.class)
    public SmsCodeSender smsCodeSender() {
        return new DefaultSmsCodeSender();
    }
}

在最後一個Bean的配置中,使用了@ConditionalOnMissingBean註解,這裡是告訴Spring,如果上下文環境中沒有SmsCodeSender介面的實現類物件,那麼就執行下面的方法進行預設的Bean建立。所以對於使用者自定義方式,只需要寫一個類實現SmsCodeSender介面,並將其標註為SpringBean即可,就可以覆蓋自帶的簡訊傳送邏輯。如果一開始使用@Component註解來進行標註了,那就無法獲得這樣自定義的效果。

至此,我們已經完成了對文章開始處的邏輯分析的所有程式碼,接下來將程式碼整合到Spring Security中,讓其能在Spring Security中得到驗證,從而實現簡訊的驗證功能。

二、將簡訊驗證碼登入方式整合到Spring Security中

相關推薦

Spring Security技術開發企業級認證授權開發簡訊驗證登入

簡訊登入也是一種常見的登入方式,但是簡訊登入的方式並沒有整合到Spring Security中,所以往往還需要我們自己開發簡訊登入邏輯,將其整合到Spring Security中,使用Spring Security來進行校驗。本文將介紹開發簡訊登入的方法,

Spring Security技術開發企業級認證授權使用Filter、Interceptor和AOP攔截REST服務

一般情況,在訪問RESTful風格的API之前,可以對訪問行為進行攔截,並做一些邏輯處理,本文主要介紹三種攔截方式,分別是:過濾器Filter、攔截器Interceptor以及面向切面的攔截方式AOP。 一、使用過濾器Filter進行攔截 使用過

Spring Security技術開發企業級認證授權開發圖形驗證介面

在設計登入模組的時候,圖形驗證碼基本上都是標配,本篇部落格重點介紹開發可重用的圖形驗證碼介面,該介面支援使用者自定義配置,比如驗證碼的長度、驗證碼圖形的寬度和高度等資訊。 本文的目標是開發一個圖形驗證碼介面,該驗證碼支援使用者自定義長度,以及生成圖片後

Spring Security技術開發企業級認證授權Spring Security的基本執行原理個性化登入實現

正如你可能知道的兩個應用程式的兩個主要區域是“認證”和“授權”(或者訪問控制)。這兩個主要區域是Spring Security的兩個目標。“認證”,是建立一個他宣告的主題的過程(一個“主體”一般是指使用者,裝置或一些可以在你的應用程式中執行動作的其他系統)

Spring Security技術開發企業級認證授權使用Swagger自動生成API文件

由於Spring Boot能夠快速開發、便捷部署等特性,相信有很大一部分Spring Boot的使用者會用來構建RESTful API。而我們構建RESTful API的目的通常都是由於多終端的原因,這些終端會共用很多底層業務邏輯,因此我們會抽象出這樣一層

Spring Security技術開發企業級認證授權環境搭建

Spring Security是一個能夠為基於Spring的企業應用系統提供宣告式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉Inversion of Contr

Spring Security技術開發企業級認證授權

iyu 復雜 sha 日誌 開發app 一個 核心概念 並發 自動 Spring Security技術棧開發企業級認證與授權網盤地址:https://pan.baidu.com/s/1mj8u6JQ 密碼: 92rp備用地址(騰訊微雲):https://share.weiy

Spring Security技術開發企業級認證授權 Spring Security開發安全的REST服務

第1章 課程導學 介紹課程內容、課程特點,使用的主要技術棧,以及學習課程所需的前置知識 1-1 導學 第2章 開始開發 安裝開發工具,介紹專案程式碼結構並搭建,基本的依賴和引數設定,開發hello world 2-1 開發環境安裝 2-2 程式碼結構介紹 2-3

軟體開發中的理想現實——夠用就好

2月21日,專案正式開始第二天。依照昨天設計的框架和介面,我們開始實現這些功能,不過似乎大家的進展都比較慢,特別是XophiiX,似乎他陷入了困境之中。具體是什麼問題呢?請看下面的介面定義:    class CReader    {        // ...    pub

使用O2OA二次開發搭建企業辦公平臺組織管理篇:關於系統角色管理

本部落格為O2OA系列教程、O2OA使用手冊,教程目錄和各章節天梯將在連載完後更新。 使用O2OA二次開發搭建企業辦公平臺(一)平臺部署篇:平臺下載和部署 使用O2OA二次開發搭建企業辦公平臺(二)平臺部署篇:埠衝突和伺服器埠配置 使用O2OA二次開發搭建企業辦公平臺(三)平臺部署篇:使用外部資料庫

計算機組成設計—— 儲存層次結構

儲存層次結構概況 這是我們非常熟悉的馮·諾依曼計算機結構, 那這其中哪些部件和儲存功能有關呢?    儲存器和外部記錄介質肯定具有儲存功能,另外還有一個自帶儲存功能的運算器,為了描述方便,我們把這些部件統稱為儲存器。那麼我們看一下計算機中對儲存器有哪些要求 ? 1、首先儲存器當

演算法設計分析

300. Longest Increasing Subsequence Given an unsorted array of integers, find the length of longest increasing subsequence. Example

Cocos2d—X遊戲開發之CCTableView詳解附原始碼

本來很早就想寫關於CCTableView的文章,但是在基本功能實現之後呢,專案需求增加導致對這個控制元件的研究必須更加深入一點. 好的,現在開始介紹一下這個控制元件,在Cocos2d—X引擎中,這是一個仿製iOS裡面的NSTableView的一個控制元件。 S1,使用這個

百度大腦人臉識別深度驗證思考之動態實時

前言 我已經厭倦了靜態圖片的識別,那些技術對我已經沒有了挑戰性。今天我們就來看看動態實時的深度識別表現如何。 攝像頭 央視 我們直接採集央視rtmp推流地址的視訊,直接進行人臉識別和即時

整合SpringMVC框架+Mybatis框架開發人力資源管理系統

實現使用者管理中的新增、刪除、修改功能 新增功能的實現 @RequestMapping(value="/insertUser") public ModelAndView addUser(@ModelAttribute User user,ModelAndView mv){

android 開發零起步學習筆記:介面切換+幾種常用介面切換效果

兩種方法實現介面的切換: 方法1、layout切換(通過setContentView切換layout) 方法2、Activity切換 方法3、Android之fragment點選切換和滑動切換 方法1、layout切換(通過setContentView切換la

【連載】物聯網全教程-從雲端到裝置---呼叫阿里雲API,獲取物的屬性。

物聯網全棧教程-從雲端到裝置(十一)一千千萬萬的物聯網裝置通過ALink協議接入到了雲端,它們不斷地按照ALink協議的規範向雲端彙報資料,同時也一直在等待伺服器下發一些資料並處理這些資料。關於物聯網裝置端如何搞定這些資料,零妖老哥會在下一篇文章中以一個型號叫做STM32F1

Linux程序啟動過程分析do_execve(可執行程式的載入和執行)---Linux程序的管理排程

execve系統呼叫 execve系統呼叫 我們前面提到了, fork, vfork等複製出來的程序是父程序的一個副本, 那麼如何我們想載入新的程式, 可以通過execve來載入和啟動新的程式。 x86架構下, 其實還實現了一個

資料結構演算法Trie字典樹

本文主要包括以下內容: Trie字典樹的基本概念 Trie字典樹的基本操作 插入 查詢 字首查詢 刪除 基於連結串列的Trie字典樹 基於Trie的Set效能對比 LeetCode相關線段樹的問題 LeetCode第208號問題 LeetCode第211

物聯網全教程-從雲端到裝置

一千千萬萬的物聯網裝置通過ALink協議接入到了雲端,它們不斷地按照ALink協議的規範向雲端彙報資料,同時也一直在等待伺服器下發一些資料並處理這些資料。關於物聯網裝置端如何搞定這些資料,零妖老哥會在下一篇文章中以一個型號叫做STM32F103的微控制器為例子進行硬體電路和程