SpringCloud微服务安全API安全 2-4 认证

Posted 鮀城小帅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud微服务安全API安全 2-4 认证相关的知识,希望对你有一定的参考价值。

1.认证

认证:登录和认证是 两个概念,比如你两周、一个月,可能只登录了一次,但认证却是每次访问都要经过的步骤。

对于图中的认证不成功,也要继续处理,这个我觉得得看业务,比如管理系统,不登录就不让你访问,但对于比如电商的商品信息,不登录,也是可以访问的。

2. 编写一个用户注册服务

2.1 根据单一职责原则创建 UserInfo 用于接口返回

单一职责原则:用户注册服务,得新建一个UserInfo类,用来接收前端传过来的注册用户信息,而最好不要使用User类直接接收。

新增方法,可以将UserInfo返回,前端可以做相应的展示。

@Data
public class UserInfo {

    private Long id ;

    private String name;

    @NotBlank(message = "用户名不能为空")
    private String username;

    @NotBlank(message = "密码不能为空")
    private String password;
}

业务层修改 UserInfo ,包括UserServiceImpl

public interface UserService {

    UserInfo create(UserInfo user);

    UserInfo update(UserInfo user);

    void delete(Long id);

    UserInfo get(Long id);

    List<UserInfo> query(String name);
}

标准三层结构: UserController ----- > UserService -----> UserRepository 。

数据处理: 

  • UserService --------> UserRepository ,查询返回 User对象
  • UserController ---------> UserService , BeanUtils.copyProperties(UserInfo,User) 处理返回 UserInfo

2.2 启动程序并添加用户

业务代码

    @Override
    public UserInfo create( @Valid UserInfo userInfo) {
        /**
         *  将密码加密成明文
         */
        User user = new User();
        BeanUtils.copyProperties(userInfo,user);
        // 加密
        user.setPassword(SCryptUtil.scrypt(user.getPassword(),32768,8,1));
        userRepository.save(user);
        userInfo.setId(user.getId());
        return userInfo;
    }

接口

    @PostMapping
    public UserInfo create(@RequestBody  @Validated UserInfo user){
        return userService.create(user);
    }

测试及结果:

3. HttpBasic认证

3.1 Basic 认证标准

在HTTP协议进行通信的过程中,HTTP协议定义了基本认证过程以允许HTTP服务器对WEB浏览器进行用户身份证的方法,当一个客户端向HTTP服务器进行数据请求时,如果客户端未被认证,则HTTP服务器将通过基本认证过程对客户端的用户名及密码进行验证,以决定用户是否合法。

具体做法是,将用户名、密码用:号隔开,然后base64编码,放在请求头的Authorization字段里,值是Basic  base64编码的字符串。

  • RequestHeader 
    • Authorization:Basic Base64(用户名:密码)

比较简单,但是安全性不高。

3.2 创建HttpBasic过滤器实现认证机制

(1)编写HttpBasic过滤器

package com.imooc.security.filter;

import com.imooc.security.user.User;
import com.imooc.security.user.UserRepository;
import com.lambdaworks.crypto.SCryptUtil;
import lombok.extern.slf4j.Slf4j;
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;

/**
 * @ClassName BasicAuthecationFilter
 * @Description TODO
 * @Author wushaopei
 * @Date 2020/10/17 14:20
 * @Version 1.0
 */
@Component
public class BasicAuthecationFilter extends OncePerRequestFilter{

    @Autowired
    private UserRepository userRepository;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 获取请求头中的认证信息
        String authHeader = request.getHeader("Authorization");
        // 判断认证信息是否存在,有就进行验证
        if(StringUtils.isNotBlank(authHeader)){
            // 截取认证信息中的basic信息
            String token64 = StringUtils.substringAfter(authHeader,"Basic ");
            String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(token,":");
            String token = new String(Base64Utils.decodeFromString(token64));
            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);
    }
}
  • 处理httpbasic认证过滤器
    httpbasic:请求头,即Authorization:Basic 加密字符串 加密字符串为Base64编码的用户名:密码字符串 Authorization Basic bGh5OmxoeWFwcA== 
    在SpringBoot里,任何实现了Filter接口的类,SpringBoot会自动把它加到web应用的过滤器链里,只要声名为Component就行了
    

(2)UserService.java 查询业务

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

(3)接口

   /**
     * @Description TODO 查一个用户
     * @param id
     * @param request
     * @param response
     * @return
     */
    @GetMapping("/{id}")
    public UserInfo get(@PathVariable Long id , HttpServletRequest request, HttpServletResponse response){
        User user = (User)request.getAttribute("user");
        if( user == null || !user.getId().equals(id)){
            throw new RuntimeException("身份认证信息异常,获取用户信息失败");
        }
        return  userService.get(id);
    }

3.3 测试认证功能

(1)未携带Basic认证的Token

插件GET请求结果为500

开发工具校验user为null,打印抛出的异常:

(2)添加Basic认证

根据以上流程添加 Basci 认证Token到请求头中。

再次请求,本次携带有Basic认证Token

3.4 是否每个请求都带上用户名、密码

在上小节中,已经验证了携带Basic的Token可以实现认证功能,那么这里要问一个问题:难道每一个请求导到服务器时都需要带上用户名、密码吗?这样合适吗?有没有更完善的方案?

以上是关于SpringCloud微服务安全API安全 2-4 认证的主要内容,如果未能解决你的问题,请参考以下文章

SpringCloud微服务安全API安全 2-2 注入攻击防护

SpringCloud微服务安全实战API安全 3-9 总结

SpringCloud微服务安全API安全 2-7 授权

SpringCloud微服务安全API安全 2-6 审计

SpringCloud微服务安全实战API安全 3-5 认证

SpringCloud微服务安全API安全 2-8 授权