SpringSecurity框架认证流程详解
1.添加依赖
在Spring Boot项目中使用Spring Security时需要添加依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.创建一个SecurityConfig.java配置类, 在里面重写configur方法
3. 在方法中配置白名单
@Override
protected void configure(HttpSecurity http) throws Exception {
//super.configure(http); 不能保留!
//当访问需要登录认证才能访问的资源时, 没有登录的时跳转到登录页面
http.formLogin().loginPage("/login.html");//弹出登录页面
//设置黑名单(需要登录才可访问的资源)
String[] urls = {"",""};
// 配置请求授权
// 如果某个请求被多次配置,按照“第一匹配原则”处理
// 应该将精确的配置写在前面,将较模糊的配置写在后面
http.authorizeHttpRequests()
.mvcMatchers(urls) //匹配某些请求路径
.authenticated() //需要通过登录认证
.permitAll(); //许可,直接放行, 即不需要登录也可以访问
.anyRequest() //任何请求,从执行效果来看,也可以视为:除了以上配置过的以外的其它请求
// 是否调用以下方法,将决定是否启用默认的登录页面
// 当未通过认证时,如果有登录页,则自动跳转到登录,如果没有登录页,则直接响应403
// http.formLogin();
// super.configure(http); // 不要调用父类的同名方法,许多默认的效果都是父类方法配置的
//关闭跨域攻击防御策略,否则所有post请求将失效 ---禁用“防止伪造的跨域攻击的防御机制”
http.csrf().disable();
}
4. 创建UserDetailsServiceImpl实现类, 在里面配置正确的用户名和密码
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired(required = false)
UserMapper mapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//通过用户输入的用户名从数据库中查询信息
UserVO userVO = mapper.selectByUserName(username);
if (userVO==null){
return null;//代表用户名不存在,此时会抛出异常
}
//模拟liubei为管理员 其它是用户
String role = userVO.getIsAdmin()==1?"ADMIN":"USER";
//如果用户输入的密码和数据库中查询到的密码不一致,则会抛出异常
List<GrantedAuthority> list =AuthorityUtils.createAuthorityList(role);
//创建自定义的UserDetails,并把后期需要用到的id和昵称保存到里面
CustomUserDetails cud = new CustomUserDetails(userVO.getIsAdmin(),
userVO.getId(),userVO.getNickName(),userVO.getImgUrl(),
username, userVO.getPassword(),list);
return cud;
}
}
//因为父类中有构造方法 此时添加@Data会让当前类添加构造方法,此方法可能会和父类的冲突所以报错
@Getter
public class CustomUserDetails extends User {
private Long id;
private String nickName;
private String imgUrl;
private Integer isAdmin;
public CustomUserDetails(Integer isAdmin,Long id,String nickName,String imgUrl,String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
this.isAdmin = isAdmin;
this.id = id;
this.nickName = nickName;
this.imgUrl = imgUrl;
}
}
5. 配置密码的加密方式
@Bean
public PasswordEncoder passwordEncoder(){
//NoOpPasswordEncoder.getInstance()获取一个无加密的实例
//return NoOpPasswordEncoder.getInstance();
//返回此加密的编码器之后, 用户输入的密码会通过此编码器加密之后再和数据库里面的密码进行比较
return new BCryptPasswordEncoder();
}
6. 配置自己的登录页面
//在SecurityConfig类中配置
http.formLogin().loginPage("/login.html");//弹出登录页面
7. 使用自己的登录页面时,Security框架的登录认证流程不会自动启动, 需要我们在UserController处理login请求时手动开启, 手动开启时需要用到认证管理器,在Security配置类中进行配置
@Bean //添加此注解的目的是为了在Controller中自动装配
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
8.在UserController中自动装配认证管理器对象,并在login方法中启动认证流程
@Autowired
AuthenticationManager manager;
@RequestMapping("login")
public ResultVO login(@RequestBody UserLoginDTO userLoginDTO){
//通过认证管理器启动Security的认证流程,返回认证结果对象
Authentication result = manager.authenticate(new UsernamePasswordAuthenticationToken(
userLoginDTO.getUserName(), userLoginDTO.getPassword()));
//将认证结果保存到Security上下文中,让Security框架记住登录状态
SecurityContextHolder.getContext().setAuthentication(result);
//代码执行到这里时代表登录成功!如果登录失败Security框架会抛出异常
return ResultVO.ok(result.getPrincipal());
}
/**
* 用户名或密码错误抛出的异常
*/
@ExceptionHandler({InternalAuthenticationServiceException.class,
BadCredentialsException.class})
public ResultVO handleAuthenticationException(AuthenticationException e){
//判断当前异常是否属于用户名不存在异常
if (e instanceof InternalAuthenticationServiceException){
log.warn("用户名不存在!");
return new ResultVO(StatusCode.USERNAME_ERROR);
}
log.warn("密码错误!");
return new ResultVO(StatusCode.PASSWORD_ERROR);
}
SpringSecurity框架认证流程描述
- 在SecurityConfig配置类中配置好白名单, 设置登录页面, 关闭跨域攻击防御策略
- 当客户端请求的路径不在白名单里面, Security框架会自动将请求重定向到登录页面
- 在login.html登录页面中向/login地址发出登录请求时,服务器中UserController里面的login方法处
理该请求 - 在login方法中通过认证管理器manager启动认证 将认证结果保存在Security上下文对象中
- 当manager启动认证流程后会自动调用UserDetailServiceImpl里面的loadUserByUsername方法,
在方法内部 调用UserMapper里面的查询方法通过用户名查询到UserVO, 如果查询不到return
null, 此时Security框架会抛出异常代表用户名不存在,需要在全局异常处理中进行处理, 如果查询到
的密码和用户输入的密码一致,则不会抛出任何异常 UserController中的login方法会执行完,给客户
端响应登录成功的信息, 如果登录的密码错误Security框架会抛出代表密码错误的异常,此时也需要
在全局异常处理类中进行处理.