UsernamePasswordAuthenticationFilter执行流程

SpringSecurity是在拦截器Interceptor前面的。
SpringSecurity走完,如果项目中设置了拦截器,还会走拦截器的。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

添加依赖后,启动项目,访问任意接口都会跳转到登录页
在这里插入图片描述
输入账号,密码,程序会先执行到UsernamePasswordAuthenticationFilter的attemptAuthentication方法

// UsernamePasswordAuthenticationFilter.class
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response){
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String username = this.obtainUsername(request);		// 从HttpServletRequest中拿到传来的的用户名参数
            username = username != null ? username : "";
            username = username.trim();
            String password = this.obtainPassword(request);		// 从HttpServletRequest中拿到传来的的密码参数
            password = password != null ? pasword : "";
            // 封装 Authentication
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);	// 试图认证
        }
    }

进入到ProviderManager这个类当中的authenticate方法。
之后调用DaoAuthenticationProvider继承的其父类public abstract class AbstractUserDetailsAuthenticationProvider的authenticate方法。
之后进入到DaoAuthenticationProvider这个类中retrieveUser方法。

    protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) {
        this.prepareTimingAttackProtection();

        try {	// 根据用户名在系统中查询用户信息
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
            } else {
                return loadedUser;
            }
        } catch (Exception var6) {
            throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
        }
    }

之后又进入到public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService这个类中的loadUserByUsername方法

    public UserDetails loadUserByUsername(String username){
    // 根据用户名在系统中查询用户信息
    // InMemory是默认内存中存着相应的用户信息
        UserDetails user = (UserDetails)this.users.get(username.toLowerCase());
        if (user == null) {
            throw new UsernameNotFoundException(username);
        } else {
			// 封装返回
            return new User(user.getUsername(), user.getPassword(), user.isEnabled(), 
            user.isAccountNonExpired(), user.isCredentialsNonExpired(), 
            user.isAccountNonLocked(), user.getAuthorities());
        }
    }

查询到的话,封装为UserDetails,又层层返回至AbstractUserDetailsAuthenticationProvider类中的authenticate方法。
之后又进入到DaoAuthenticationProvider类中的additionalAuthenticationChecks方法。

// userDetails是在系统中查到的用户信息, authentication是登录时填写的信息进行封装
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication){
        if (authentication.getCredentials() == null) {
            this.logger.debug("Failed to authenticate since no credentials provided");
            throw new BadCredentialsException(this.messages
            .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
        // 比对密码
            String presentedPassword = authentication.getCredentials().toString();
            if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
                this.logger.debug("Failed to authenticate since password does not match stored value");
                throw new BadCredentialsException(this.messages
                .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            }
        }
    }

比对成功之后,又返回到AbstractUserDetailsAuthenticationProvider类中的authenticate方法,调用DaoAuthenticationProvider类中的createSuccessAuthentication方法
将从系统中查到的UserDetails信息(改名为principal),以及authentication(用户登陆时填的信息),以及一个MutableUserDetails(将从系统中查到的UserDetails信息,将其中的密码进行Bcrpt加密,封装为MutableUserDetails对象)这三者进行整合,最后封装信息,返回一个UsernamePasswordAuthenticationToken。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一:RestApi接口增加JWT认证功能<br/> 用户填入用户名密码后,与数据库里存储的用户信息进行比对,如果通过,则认证成功。传统的方法是在认证通过后,创建sesstion,并给客户端返回cookie。 现在我们采用JWT来处理用户名密码的认证。区别在于,认证通过后,服务器生成一个token,将token返回给客户端,客户端以后的所有请求都需要在http头中指定该token。 服务器接收的请求后,会对token的合法性进行验证。验证的内容包括: 内容是一个正确的JWT格式 检查签名 检查claims 检查权限 处理登录 创建一个类JWTLoginFilter,核心功能是在验证用户名密码正确后,生成一个token,并将token返回给客户端: 该类继承自UsernamePasswordAuthenticationFilter,重写了其中的2个方法: attemptAuthentication :接收并解析用户凭证。 successfulAuthentication :用户成功登录后,这个方法会被调用,我们在这个方法里生成token。 二:授权验证 用户一旦登录成功后,会拿到token,后续的请求都会带着这个token,服务端会验证token的合法性。 创建JwtAuthenticationFilter类,我们在这个类中实现token的校验功能。 该类继承自BasicAuthenticationFilter,在doFilterInternal方法中,从http头的Authorization 项读取token数据,然后用Jwts包提供的方法校验token的合法性。 如果校验通过,就认为这是一个取得授权的合法请求。 三:SpringSecurity配置 通过SpringSecurity的配置,将上面的方法组合在一起。 这是标准的SpringSecurity配置内容,就不在详细说明。注意其中的 .addFilter(new JWTLoginFilter(authenticationManager())) .addFilter(new JwtAuthenticationFilter(authenticationManager())) 这两行,将我们定义的JWT方法加入SpringSecurity的处理流程中。 四:简单测试 下面对我们的程序进行简单的验证:<br/> 1.请求获取用户列表接口:http://localhost:8080/users/userList接口,会收到403错误<br/> { "timestamp": 1518333248079, "status": 403, "error": "Forbidden", "message": "Access Denied", "path": "http://localhost:8080/users/userList" } curl http://localhost:8080/users/userList<br/> 原因就是因为这个url没有授权,所以返回403<br/> ![输入图片说明](https://gitee.com/uploads/images/2018/0211/154022_8d9806ae_130820.png "jwt-1.png") 2.注册一个新用户<br/> curl -H "Content-Type: application/json" -X POST -d '{<br/> "username": "admin",<br/> "password": "password"<br/> }' http://localhost:8080/users/signup<br/> ![输入图片说明](https://gitee.com/uploads/images/2018/0211/154042_74fb2aa6_130820.png "jwt-2.png") 3.登录,会返回token,在http header中,Authorization: Bearer 后面的部分就是token<br/> curl -i -H "Content-Type: application/json" -X POST -d '{<br/> "username": "admin",<br/> "password": "password"<br/> }' http://localhost:8080/login<br/> 温馨提醒:这里的login方法是spring specurity框架提供的默认登录url ![输入图片说明](https://gitee.com/uploads/images/2018/0211/154308_9576ce90_130820.png "jwt-3.png") 4.用登录成功后拿到的token再次请求/users/userList接口<br/> 4.1将请求中的XXXXXX替换成拿到的token<br/> 4.2这次可以成功调用接口了<br/> curl -H "Content-Type: application/json"<br/> -H "Authorization: Bearer XXXXXX"<br/> "http://localhost:8080/users/userList" ![输入图片说明](https://gitee.com/uploads/images/2018/0211/154315_241cd6b2_130820.png "jwt-4.png")
Spring Security是一个基于Spring框架的安全框架,它提供了一系列的安全服务和机制,可以帮助我们实现各种安全需求,比如身份认证、访问控制、安全日志等。 Spring Security执行流程如下: 1. 客户端发送请求:客户端通过浏览器或者移动端向服务器发送请求。 2. 进入过滤器链:当请求进入到服务器后,会先经过Spring Security的过滤器链,这个过滤器链有多个过滤器,每个过滤器都有不同的功能,比如身份认证、访问控制等。 3. 执行身份认证:在过滤器链中,如果存在身份认证的过滤器,则会执行身份认证,比如UsernamePasswordAuthenticationFilter过滤器,它会从请求中获取用户名和密码,然后进行身份认证。 4. 认证成功/失败处理:如果身份认证成功,则会生成一个安全上下文对象,该对象包含了当前用户的信息和权限等,然后会将该安全上下文对象存储到SecurityContextHolder中。如果身份认证失败,则会返回一个错误信息。 5. 执行访问控制:在过滤器链中,如果存在访问控制的过滤器,则会执行访问控制,比如AccessDecisionManager决策管理器,它会根据当前用户的安全上下文对象和请求的资源进行访问控制决策。 6. 访问控制成功/失败处理:如果访问控制决策成功,则会继续执行后续的操作,比如请求资源的处理。如果访问控制决策失败,则会返回一个错误信息。 7. 返回响应:最后,服务器会将响应结果返回给客户端,结束整个请求响应流程。 以上就是Spring Security执行流程,通过这个流程,我们可以了解到Spring Security是如何进行身份认证和访问控制的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值