Spring cloud微服務安全實戰-3-5 API安全機制之認證(2)


基於Http協議的認證方式有很多。本節我們只講一個最簡單的HttpBasic認證。聰明就可以看出來,這是一個最基礎的認證,好處是簡單方便,所有的主流瀏覽器都支持,問題就是並不是非常安全的,但是幫我們大家理解認證這個概念是足夠的。

首先要對認證信息做Base64的加密,加密之前要把這兩個信息組合起來。用戶名冒號密碼組合成這樣一個字符串。然后拿這個字符串做Base64的字符串的加密。並生成一個字符串。

把生成的串放到http請求的 請求頭里面。帶個前綴 Basic +空格 + 加密后的字符串。

開始寫代碼


先加上字符串處理的工具的引用。commons-lang3這里面有個字符串處理的工具類

 

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>

 


建一個新的過濾器BaseicAuthecationFilter

繼承OncePerRequestFilter


@Component聲明稱一個組件,這樣SpringBoot會自動把他加到應用的過濾器鏈上。

注入UserRepository



用java.util這個包下的也可以,用Base64Utils這個也可以。

根據冒號拆分成兩個字符串

創建這個根據UserName查詢的方法



之前寫的findByName刪掉。

User findByUsername(String username);

 


StringUtils用的都是apache.commons.lang3這個包下的類。

package com.imooc.security.filter;

import com.imooc.security.user.User;
import com.imooc.security.user.UserRepository;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class BasicAuthecationFilter extends OncePerRequestFilter {

    @Autowired
    private UserRepository userRepository;

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authHeader = request.getHeader("Authorization");
        if(StringUtils.isNotBlank(authHeader)){
            String token64 = StringUtils.substringAfter(authHeader, "Basic ");
            String token = new String(Base64Utils.decodeFromString(token64));//解碼
            //根據冒號拆分成兩個字符串
            String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(token, ":");
            String username = items[0];
            String password = items[1];
            User user = userRepository.findByUsername(username);
            if(user!=null && StringUtils.equals(password,user.getPassword())){
                request.setAttribute("user",user);
            }
        }
        filterChain.doFilter(request,response);
    }
}
Filter代碼

 



因為我們還沒有寫審計等的代碼,這里我們先在controller里面寫代碼,先看到效果。
user里面getId獲取到id和當前的接口傳遞過來的參數id對象,如果不相等就拋出異常。

 

遇到的錯誤

出現問題的原因

修改成Long即可

 

繼續


在User實體類里面。寫一個方法,buildInfo,。把User對象轉換成UserInfo對象。

 
         
public UserInfo buildInfo(){
UserInfo info=new UserInfo();
BeanUtils.copyProperties(this,info);
return info;
}
 

 


JPA提供的findById默認的返回類型是一個Optional的類型。

UserServiceImpl 。所以要先get()再調用buildInfo()

 public UserInfo get(Long id) {
        return userRepository.findById(id).get().buildInfo();
    }

 

運行測試

現在在請求頭里面根本沒有帶認證信息。這里是個get請求。查詢id為1的用戶信息

{
    "timestamp": "2019-12-10T09:05:46.377+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "身份認證信息異常,獲取用戶信息失敗",
    "path": "/users/1"
}

 


在過濾器里面,相當於這一段完全沒有執行

經過過濾器會進入到controller里面。在Arttribute里面拿到的是空。所以就拋出了異常。

   @GetMapping("/{id}")
    public UserInfo get(@PathVariable Long id, HttpServletRequest request){
        User user=(User) request.getAttribute("user");


        if(user == null || !user.getId().equals(id)) {
            throw new RuntimeException("身份認證信息異常,獲取用戶信息失敗");
        }
        return userService.get(id);
    }

 







發出去的完整的信息里面已經帶了請求頭的信息了。但是 依然報了異常

這是因為創建的用戶的id是5 但是回去的用戶信息id是1。所以造成了id的不匹配。

把請求的id改成5


這樣用戶信息成功返回

以上就是認證起作用的一個簡單的例子。
 

postman里面的設置

 

結束

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM