一.简介
这篇文章来学习下security的认证方式其中的HTTP摘要认证
二.Spring Security的认证方式
2.1什么是认证
认证:
 就是用来判断系统中是否存在某用户,并判断该用户的身份是否合法的过程,解决的其实是用户登录的问题。认证的存在,是为了保护系统中的隐私数据与资源,只有合法的用户才可以访问系统中的资源。
2.2认证的方式
在Spring Security中,常见的认证方式可以分为HTTP层面和表单层面,常见的认证方式如下:
- HTTP基本认证;
- Form表单认证
- HTTP摘要认证;
这篇文章先讲HTTP摘要认证
三. HTTP摘要认证
3.1Form表单认证简介
HTTP摘要认证和HTTP基本认证一样,也是在RFC2616中定义的一种认证方式,它的出现是为了弥补HTTP基本认证存在的安全隐患,但该认证方式也并不是很安全。HTTP摘要认证会使用对通信双方来说都可知的口令进行校验,且最终以密文的形式来传输数据,所以相对于基本认证来说,稍微安全了一些。
3.2HTTP摘要认证模型
HTTP摘要认证与基本认证类似,基于简单的“挑战-回应”模型。当我们发起一个未经认证的请求时,服务器会返回一个401回应,并给客户端返回与验证相关的参数,期待客户端依据这些参数继续做出回应,从而完成整个验证过程。
3.3HTTP摘要认证核心参数
服务端给客户端返回的验证相关参数如下:
- username: 用户名。
- password: 用户密码。
- realm: 认证域,由服务器返回。
- opaque: 透传字符串,客户端应原样返回。
- method: 请求的方法。
- nonce: 由服务器生成的随机字符串,包含过期时间(默认过期时间300s)和密钥。
- nc: 即nonce-count,指请求的次数,用于计数,防止重放攻击。qop被指定时,nc也必须被指定。
 8.cnonce: 客户端发给服务器的随机字符串,qop被指定时,cnonce也必须被指定。
- qop: 保护级别,客户端根据此参数指定摘要算法。若取值为 auth,则只进行身份验证;若取值为auth-int,则还需要校验内容完整性,默认的qop为auth。
- uri: 请求的uri。
- response: 客户端根据算法算出的摘要值,这个算法取决于qop。
- algorithm: 摘要算法,目前仅支持MD5。
- entity-body: 页面实体,非消息实体,仅在auth-int中支持。
通常服务器端返回的数据包括realm、opaque、nonce、qop等字段,如果客户端需要做出验证回应,就必须按照一定的算法得到一些新的数据并一起返回。在以上各种参数中,对服务器而言,最重要的字段是nonce;对客户端而言,最重要的字段是response。
四. 创建SpringSecurity项目
参考之前的文章,这边不做叙述。
五.代码实现
5.1创建SecurityConfig配置类
创建SecurityConfig类,继承自WebSecurityConfigurerAdapter父类,该类的作用如下:
- 验证所有请求;
- 允许用户使用表达登录进行身份验证;
- 允许用户使用Http基本认证。
代码如下:
@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Autowired
    private DigestAuthenticationEntryPoint digestAuthenticationEntryPoint;
 
    @Autowired
    private MyUserDetailsService userDetailsService;
 
    //配置认证入口端点,主要是设置认证参数信息
    @Bean
    public DigestAuthenticationEntryPoint digestAuthenticationEntryPoint(){
        DigestAuthenticationEntryPoint point=new DigestAuthenticationEntryPoint();
        point.setKey("Security Demos");
        point.setRealmName("demaxiya");
        point.setNonceValiditySeconds(500);
        return point;
    }
 
    public DigestAuthenticationFilter digestAuthenticationFilter(){
        DigestAuthenticationFilter filter=new DigestAuthenticationFilter();
        filter.setAuthenticationEntryPoint(digestAuthenticationEntryPoint);
        filter.setUserDetailsService(userDetailsService);
        return filter;
    }
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**")
                .hasRole("ADMIN")
                .antMatchers("/user/**")
                .hasRole("USER")
                .antMatchers("/visitor/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf()
                .disable()
                //当未认证时访问某些资源,则由该认证入口类来处理.
                .exceptionHandling()
                .authenticationEntryPoint(digestAuthenticationEntryPoint)
                .and()
                //添加自定义过滤器到过滤器链中
                .addFilter(digestAuthenticationFilter());
    }
 
}
5.2创建web接口
IndexController 类的代码如下:
@RestController
public class IndexController {
 
    @GetMapping("/admin/hello")
    public String helloAdmin() {
 
        return "hello, admin";
    }
 
    @GetMapping("/user/hello")
    public String helloUser() {
 
        return "hello, user";
    }
 
    @GetMapping("/visitor/hello")
    public String helloVisitor() {
 
        return "hello, visitor";
    }
}
5.3yml配置文件
创建一个application.yml配置文件,配置如下:
spring:
  security:
    user:
      name: demaxiya
      password: 123
5.4MyUserDetailService 类
MyUserDetailService类的代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Objects;
@Service
public class MyUserDetailService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.loadUserByUserName(username);
        if (Objects.isNull(user)) {
            throw new UsernameNotFoundException("user is null");
        }
        user.setRoles(userMapper.getRolesByUid(user.getId()));
        return user;
    }
}
六.功能验证
6.1启动项目
接着来访问一下需要认证的接口,比如/admin/hello接口,这时候会发现浏览器弹出了一个用户名密码的认证窗口。然后我们在浏览器中可以看到HTTP摘要认证信息,realm是我们自定义的“demaxiya”,qop是默认的“auth”方式。截图如下:
 
 由此可见,此时摘要认证的方式已经生效。
七.HTTP摘要认证弊端
HTTP摘要认证与HTTP基本认证一样,都是基于HTTP层面的认证方式,也不能使用Session对象,因而也不支持Remember-Me功能。该方式虽然解决了HTTP基本认证中以明文传输密码的问题,但并未解决密码明文存储的问题,所以依然有安全隐患,所以在开发中,摘要认证的方式也不怎么使用。



















