SpringSecurity的介绍与使用

249 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

1、前言

在之前的两篇分享中,我们详细介绍了 Shiro 的使用,本篇为大家分享另一个主流安全认证框架 SpringSecurity 的使用。

2、什么是Spring Security?

在其官网上是这么介绍的:

1、Spring Security是一个功能强大、高度可定制的身份验证和访问控制框架。它是保护基于Spring的应用程序的标准。

2、SpringSecurity是一个专注于为Java应用程序提供身份验证和授权的框架。与所有Spring项目一样,Spring Security的真正强大之处在于它可以轻松扩展以满足定制需求。

3、与Shiro的区别

1、Spring Security属于Spring家族,与Spring项目结合较好,如果项目是使用SpringBoot开发的,使用起来更加方便;社区资源也比Shiro更丰富一些;

2、Spring Security相较于Shiro更复杂一些,没有Shiro清晰易懂;

3、Shiro可以用户非web项目;

4、快速入门

4.1、pom文件引入依赖

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

Spring Security 有一个自带的默认登录页,引入依赖后,我们访问任何借口都会先跳转到默认登录页,现在我们就把默认登录页更换成自己的。

4.2、配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableConfigurationProperties(SecurityProperty.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(securityUserDetailsService).passwordEncoder(passwordEncoder);
    } 

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() 
                // 登录后才能访问
                .anyRequest().authenticated()
                .and() 
                .httpBasic()
                .authenticationEntryPoint(securityAuthenticationEntryPoint)
                .and()
                .formLogin()
                // 登录页面
                .loginPage("/login")
                .loginProcessingUrl("/login")
                // 处理登录成功的handler
                .successHandler(securityAccessSuccessHandler)
                // 处理登录失败的handler
                .failureHandler(securityAccessFailureHandler)
                .and()
                .logout()
                .addLogoutHandler(securityLogoutHandler)
                // 退出登录删除缓存
                .deleteCookies(AUTH) 
                .logoutSuccessHandler(securityAccessLogoutHandler)
                .and()
                .exceptionHandling()
                // 没有权限的处理
                .accessDeniedHandler(securityAccessDeniedHandler)
                .and()
                .sessionManagement()
                .sessionFixation()
                .migrateSession()
                // 使用session时才创建
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        //  iframe 跨域
        http.headers().frameOptions().disable();
    }
}

4.3、实现UserDetailsService接口

重写其中的方法,进行用户名密码的校验;

@Component
public class MyUserDetailsServiceImpl implements UserDetailsService { 

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser sysUser = sysUserService.getUserByUsername(username);
        if (sysUser == null) {
            throw new UsernameNotFoundException("用户名密码错误!");
        }
        List<SysMenu> MenuList = sysMenuMapper.queryByUsername(username);
        List<SysRole> roleList = sysRoleMapper.queryByUsername(username);
        sysUser.setMenuList(MenuList);
        sysUser.setRoles(roleList);
        //返回的SysUser需要实现UserDetails接口
        return sysUser;
    }

}

4.4、登录成功/失败的处理

某些情况下,在用户engulf成功后需要做一些额外操作,比如说记录用户的登录日志等信息;

/**
*登陆成功后处理
*/
@Component
public class SecurityAuthenticationSuccessHandler implements AuthenticationSuccessHandler { 
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { 
        LOGGER.info("登陆成功");
        //TODO 记录用户登录日志
        SysUser currentUser = (SysUser) authentication.getPrincipal();
        currentUser.setLastTime(LocalDateTime.now());
        request.getSession().setAttribute("currentUser", authentication.getPrincipal());  
    }
}

/**
*登陆失败后处理
*/  
@Component
public class SecurityAuthenticationFailureHandler implements AuthenticationFailureHandler { 

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {

        Result result = Result.fail(500, "登录失败");

        //TODO 记录具体失败原因、失败日志,多次失败后,锁定该用户
        if (e instanceof UsernameNotFoundException) {
            result.setMsg("用户名不存在");
        }
        if (e instanceof LockedException) {
            result.setMsg("用户冻结");
        } 
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(result));
    }
}