一:自定义认证
1.自定义认证需要实现 spring security 提供的 UserDetailService 接口
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
2.接口的 loadUserByUsername 方法返回一个 UserDetails 接口。
public interface UserDetails extends Serializable {
/**
*获取用户包含的权限,返回权限集合,权限是一个继承了GrantedAuthority的对象;
*/
Collection<? extends GrantedAuthority> getAuthorities();
/**
*密码
*/
String getPassword();
/**
* 用户名
*/
String getUsername();
/**
* 用于判断账户是否未过期,未过期返回true反之返回false;
*/
boolean isAccountNonExpired();
/**
* 判断账户是否未锁定;
*/
boolean isAccountNonLocked();
/**
* 判断用户凭证是否没过期,即密码是否未过期;
*/
boolean isCredentialsNonExpired();
/**
* 判断用户是否可用
*/
boolean isEnabled();
}
自定义实现 UserDetails /UserDetailsService
@Configuration
public class MyUserDetailService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 模拟数据库获取用户数据 getUserByName(username);
String password = "123456";
// 需要加密
password = passwordEncoder.encode(password);
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
// 模拟权限
List<GrantedAuthority> admin = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
// 返回 security 提供的 UserDetails 接口实现类 User
return new User(username, password, enabled,
accountNonExpired, credentialsNonExpired,
accountNonLocked, admin);
}
}
MySecurityConfig 配置注入加密 Bean
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
二: 替换默认登录页面
在配置文件中新增:
@Override
protected void configure(HttpSecurity http) throws Exception {
// httpBasic 认证方式
// http.httpBasic()
// 表单方式登录
http.formLogin()
// 自定义登录页
.loginPage("login.html")
// 登录接口
.loginProcessingUrl("/login")
.and()
// 授权配置
.authorizeRequests()
// 放开 login.html的请求
.antMatchers("/login.html").permitAll()
// 所有请求
.anyRequest()
// 都需要认证
.authenticated();
}
.loginPage(“/login.html”)指定了跳转到登录页面的请求URL,.loginProcessingUrl(“/login”)对应登录页面form表单的action=”/login”,.antMatchers(“/login.html”).permitAll()表示跳转到登录页面的请求不被拦截,否则会进入无限循环。
三: 处理成功和失败
Spring Security有一套默认的处理登录成功和失败的方法:当用户登录成功时,页面会跳转会引发登录的请求,比如在未登录的情况下访问http://localhost:8080/hello,页面会跳转到登录页,登录成功后再跳转回来;登录失败时则是跳转到Spring Security默认的错误提示页面。下面我们通过一些自定义配置来替换这套默认的处理机制。
1.自定义登录成功逻辑
要改变默认的处理成功逻辑很简单,只需要实现org.springframework.security.web.authentication.AuthenticationSuccessHandler
接口的onAuthenticationSuccess
方法即可:
@Component
public class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {
@Autowired
private ObjectMapper mapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(mapper.writeValueAsString(authentication));
}
}
可以重定向跳转到index 接口:
@Component
public class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {
@Autowired
private ObjectMapper mapper;
/** 跳转重定向 */
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
redirectStrategy.sendRedirect(request, response, "/index");
}
}
@RequestMapping("/index")
public Object index(){
/** spring security 提供的上下文获取用户信息 */
return SecurityContextHolder.getContext().getAuthentication();
}
2.自定义登录失败逻辑
和自定义登录成功处理逻辑类似,自定义登录失败处理逻辑需要实现org.springframework.security.web.authentication.AuthenticationFailureHandler的onAuthenticationFailure方法:
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {、
@Autowired
private ObjectMapper mapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException {
// AuthenticationException 有很多实现类,不同的登录失败原因对应不同的异常
// 此处可以根据异常信息,对应返回友好的提示信息、账户密码错误、用户已禁用等
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));
}
}
同样,配置类需要新增:
@Autowired
private MyAuthenticationFailureHandler authenticationFailureHandler;
.......
http.formLogin()
// 自定义登录页
.loginPage("/login.html")
// 登录接口
.loginProcessingUrl("/login")
// 处理登录成功
.successHandler(authenticationSucessHandler)
// 处理登录失败
.failureHandler(authenticationFailureHandler)
........