SpringSercurity基本原理

原创
2019/06/07 16:42
阅读数 8.8K

spring sercurity 基础


环境配置:

win10

idea 2019.1

jdk1.8

spring cloud Finchley.RELEASE

spring-boot 2.0.2.RELEASE

git && gitee.com


基本原理

一系列的过滤器组成的链路.如下图:


FilterSercurityInterceptor就是spring security处理鉴权的入口,在访问REST api 前都会经过这个过滤器,如果通过了鉴权才会跳转到对应api的入口

下面来分析在默认开启spring security的情况下各种场景的一些执行路径,通过流程图展示出来;

对应操作: 打断点在FilterSecurityInterceptor.doFilter()上,即可追踪其执行路径

未登录,请求REST api

已登录,请求REST api,权限不够

已登录,请求REST api,权限足够


自定义用户认证逻辑

自定义认证配置信息:

需要继承WebSecurityConfigurerAdapter,并重写其configure()方法.

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 说明: 重写security的配置方法
     * @author suwenguang
     * @date 2019/6/7
     * @return void <- 返回类型
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置
        http.formLogin()
                .and()
                .authorizeRequests()
                .anyRequest()
                .authenticated();
    }
}

处理用户信息获取逻辑

实现UserDetailsService接口

@Component
@Slf4j
public class UserCertificationService implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("用户名:{}", username);
        log.info("密码:{}",passwordEncoder.encode("123456"));
        //根据username到数据库获取用户信息
        return new User(username, passwordEncoder.encode("123456")
                ,true,true,true,true,
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin,user"));
    }
}

处理用户校验逻辑

利用UserDetails

UserDetails是个接口,可以实现等方式创建自己系统的子类,以达到扩展的效果.当然也可以使用自带的. org.springframework.security.core.userdetails.User

UserDetails内部提供如下的四种状态

  • 账号是否已过期 isAccountNonExpired
  • 账号是否锁定 isAccountNonLocked
  • 密码是否已过期 isCredentialsNonExpired
  • 账号是否启用 isCredentialsNonExpired


处理密码加密解密

PasswordEncoder

配置一个PasswordEncoder

新建PasswordEncoderConfig.java

@Configuration
public class PasswordEncoderConfig {
    /**
     * 说明: 注入一个明文密码的加解密
     * @author suwenguang
     * @date 2019/6/7
     * @return org.springframework.security.crypto.password.PasswordEncoder <- 返回类型
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

个性化用户认证

form-login属性详解

form-login是spring security命名空间配置登录相关信息的标签,它包含如下属性:

  1. login-page 自定义登录页url,默认为/login
  2. login-processing-url 登录请求拦截的url,也就是form表单提交时指定的action
  3. default-target-url 默认登录成功后跳转的url
  4. always-use-default-target 是否总是使用默认的登录成功后跳转url
  5. authentication-failure-url 登录失败后跳转的url
  6. username-parameter 用户名的请求字段 默认为userName
  7. password-parameter 密码的请求字段 默认为password
  8. authentication-success-handler-ref 指向一个AuthenticationSuccessHandler用于处理认证成功的请求,不能和default-target-url还有always-use-default-target同时使用
  9. authentication-success-forward-url 用于authentication-failure-handler-ref
  10. authentication-failure-handler-ref 指向一个AuthenticationFailureHandler用于处理失败的认证请求
  11. authentication-failure-forward-url 用于authentication-failure-handler-ref
  12. authentication-details-source-ref 指向一个AuthenticationDetailsSource,在认证过滤器中使用

自定义登录界面

  1. 设置 上述属性login-page 在请求需要登录认证的api,发现用户没有经过登录认证,spring security 会自动跳转到配置的login-page的路由(方法,url,controller...)
  2. 如果是rest api 服务(前后端分离),不需要返回html页面,可以在指定的 login-page 直接返回json格式的响应
  3. 如果是需要返回html页面,也可以在 login-page 的controller返回页面
  4. login-page 的controller对请求作出判断,选择不同的返回方式即可

自定义登录成功处理

前后端分离的情况下,登录请求,客户端需要的是一个rest的返回结果,而不是跳转url,这就需要我们去做一些自定义的配置

  1. 创建一个类实现AuthenticationSuccessHandler接口,实现onAuthenticationSuccess()方法,把Authentication信息包装成json返回.

新建SystemAuthenticationSuccessHandler.java

@Component
@Slf4j
public class SystemAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    /**
     * 说明:登录成功后,执行的方法
     * @author suwenguang
     * @date 2019/6/8
     * @return void <- 返回类型
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        log.info("登录成功:{}", JSON.toJSONString(authentication));
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        Response res = ResponseBuilder.build(ResponseEnums.SIMPLE_SUCCESS, authentication);
        writer.write(JSON.toJSONString(res));
    }
}
  1. 在spring security config类里面配置上successHandler(),把刚刚的类作为参数传入即可

修改SecurityConfig.java

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private SystemAuthenticationSuccessHandler systemAuthenticationSuccessHandler;
    /**
     * 说明: 重写security的配置方法
     * @author suwenguang
     * @date 2019/6/7
     * @return void <- 返回类型
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置
        http.formLogin()
                //自定义登录方法
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                //自定义登录认证成功处理器
                .successHandler(systemAuthenticationSuccessHandler)
                .and()
                .authorizeRequests()
                //配置不需要认证的路由
                .antMatchers("/login.html","**static**").permitAll()
                //所有路由都要认证
                .anyRequest().authenticated()
                //禁用跨站攻击防护机制
                .and().csrf().disable()
                ;
    }
}

自定义登录失败处理

道理同上,但是这次需要实现的接口变成了AuthenticationFailureHandler,其他步骤几乎同上

下面贴上处理的代码

新建SystemAuthenticationFailHandler.java

@Component
@Slf4j
public class SystemAuthenticationFailHandler implements AuthenticationFailureHandler {
    /**
     * 说明: 认证失败后,执行的方法
     * @author suwenguang
     * @date 2019/6/8
     * @return void <- 返回类型
     */
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        log.info("登录失败:{}", exception.getMessage());
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        Response res = ResponseBuilder.build(ResponseEnums.LOGIN_FAIL, exception.getMessage());
        writer.write(JSON.toJSONString(res));
    }
}

修改SecurityConfig.java

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    /**
     * 登录成功的处理器
     **/
    @Autowired
    private SystemAuthenticationSuccessHandler systemAuthenticationSuccessHandler;

    /**
     * 登录失败的处理器
     **/
    @Autowired
    private SystemAuthenticationFailHandler systemAuthenticationFailHandler;
    /**
     * 说明: 重写security的配置方法
     * @author suwenguang
     * @date 2019/6/7
     * @return void <- 返回类型
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置
        http.formLogin()
                //自定义登录方法
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                //自定义登录认证成功处理器
                .successHandler(systemAuthenticationSuccessHandler)
				//自定义登录失败处理器
                .failureHandler(systemAuthenticationFailHandler)
                .and()
                .authorizeRequests()
                //配置不需要认证的路由
                .antMatchers("/login.html","**static**").permitAll()
                //所有路由都要认证
                .anyRequest().authenticated()
                //禁用跨站攻击防护机制
                .and().csrf().disable()
                ;
    }
}

2019-06-08 20:54:47 星期六

展开阅读全文
打赏
0
5 收藏
分享
加载中
更多评论
打赏
0 评论
5 收藏
0
分享
返回顶部
顶部