基於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); } }
因為我們還沒有寫審計等的代碼,這里我們先在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里面的設置
結束