持续创作,加速成长!这是我参与「掘金日新计划 · 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));
}
}