在实际开发中,一般是将用户名在客户端输入的账号,密码和数据库的账号密码比对,springsecurity也是,只不过它做得有点绕,大体流程就是AuthenticationProvider接收客户端用户输入的用户名密码,保存在authentication对象里面,再拿authentication对象里面的用户名通过UserDetailsService类去数据库查询用户用户名,密码,权限等等信息,最后再拿数据库的对象的密码和客户端输入的密码进行比较,ok,我们直接看代码,然后分析,
实体类信息
用户实体类
public class UserEntity { private String userId; private String avatar; private String userName; private String password; private String name; private String birthday; private int sex; private String email; private String phone; private String deptid; private int isenale; private String addTime; private String addUser; private String modTime; private String modUser; private List<Role> roles; }
角色实体类
public class Role { private Long roleId; private String roleName; private String roleDesc; private int isenale; private String addTime; private String addUser ; private String modTime; private String modUser ; }
用户角色关系表,里面存了用户的Id,角色的id
- 可以看到我们自定义的AuthenticationProvider类里面需要实现authenticate(Authentication authentication),这个方法里面的参数authentication就有保存我们客户端输入的用户名密码
@Component public class MyAuthenticationProvider implements AuthenticationProvider { private static final Logger logger = LoggerFactory.getLogger(MyAuthenticationProvider.class); @Autowired MyUserDetailsService myUserDetailsService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { try{ // 根据用户输入的用户名获取该用户名已经在服务器上存在的用户详情,如果没有则返回null UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(authentication.getName()); logger.info("数据库服务器上已经保存的用户名是:" + userDetails.getUsername()); logger.info("数据库服务器上保存的该用户名对应的密码是: " + userDetails.getPassword()); logger.info("数据库服务器上保存的该用户对应的权限是:" + userDetails.getAuthorities()); //判断用户输入的密码和服务器上已经保存的密码是否一致 if(authentication.getCredentials().equals(userDetails.getPassword())){ logger.info("author success"); //如果验证通过,将返回一个UsernamePasswordAuthenticaionToken对象 return new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials(), userDetails.getAuthorities()); } }catch (Exception e){ logger.error("author failed, the error message is: " + e); throw e; } //如果验证不通过将抛出异常或者返回null return null; } @Override public boolean supports(Class<?> authentication) { return true; } }
- 我们将authentication对象里面保存的用户名拿出来,再通过UserDetailsService对象去数据库检索,这里我是自定义的UserDetailsService
// 根据用户输入的用户名获取该用户名已经在服务器上存在的用户详情,如果没有则返回null UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(authentication.getName());
判断用户输入的密码和服务器上已经保存的密码是否一致,如果密码一致那么验证成功
//判断用户输入的密码和服务器上已经保存的密码是否一致 if(authentication.getCredentials().equals(userDetails.getPassword())){ logger.info("author success"); //如果验证通过,将返回一个UsernamePasswordAuthenticaionToken对象 return new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials(), userDetails.getAuthorities()); }
- 自定义UserDetailsService通过用户前端输入的用户名获取数据库用户名信息(包括用户名,密码,和权限/角色)
ackage org.spring.springcloud.config.springSecurity.authentication; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.spring.springcloud.domain.Role; import org.spring.springcloud.domain.UserEntity; import org.spring.springcloud.service.UserRoleService; import org.spring.springcloud.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; /** * 功能:根据前端页面传入的username来查询数据的用户以及角色 */ @Component public class MyUserDetailsService implements UserDetailsService { private static final Logger logger = LoggerFactory.getLogger(UserDetailsService.class); @Autowired UserService userService; @Autowired UserRoleService userRoleService; @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { logger.info("MyUserDetailsService"); //数据库连的user UserEntity userEntity; try { userEntity = userService.findUserByUserName(userName); logger.info("服务器用户名为="+userEntity.getUserName()); } catch (Exception e) { throw new UsernameNotFoundException("查询报错"); } if(userEntity == null) { throw new UsernameNotFoundException("用户不存在"); } else { try { List<Role> roles=userEntity.getRoles(); List<SimpleGrantedAuthority> simpleGrantedAuthorities = createAuthorities(roles); return new User(userEntity.getUserName(), userEntity.getPassword(), simpleGrantedAuthorities); } catch (Exception e) { throw new UsernameNotFoundException("查询用户权限报错"); } } } /** * 权限字符串转化 * * 如 "USER,ADMIN" -> SimpleGrantedAuthority("USER") + SimpleGrantedAuthority("ADMIN") */ private List<SimpleGrantedAuthority> createAuthorities(List<Role> roles){ List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>(); for (Role role : roles) { simpleGrantedAuthorities.add(new SimpleGrantedAuthority(role.getRoleName())); } return simpleGrantedAuthorities; } }
- 最后我们将我们自定义的类添加的配置文件,这样spring-security才会在认证过程中使用我们自己定义的
/** * 添加 UserDetailsService, 实现自定义登录校验 */ @Override protected void configure(AuthenticationManagerBuilder builder) throws Exception{ builder.userDetailsService(myUserDetailsService); builder.authenticationProvider(provider); }
- 上面,我们已经认证成功了,那么认证成功之后我们要干嘛,这就得用到AuthenticationSuccessHandler类,这里我也是使用自己自定义的,看代码
@Component public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { httpServletResponse.setContentType("application/json;charset=utf-8"); PrintWriter out = httpServletResponse.getWriter(); String s = "{\"status\":\"success\",\"msg\":\"登录认证成功\"}"; out.write(s); out.flush(); out.close(); } }
注意:认证是由 AuthenticationManager 来管理的,但是真正进行认证的是 AuthenticationManager 中定义的 AuthenticationProvider,当我们使用 authentication-provider 元素来定义一个 AuthenticationProvider 时,如果没有指定对应关联的 AuthenticationProvider 对象,Spring Security 默认会使用 DaoAuthenticationProvider。DaoAuthenticationProvider 在进行认证的时候需要一个 UserDetailsService 来获取用户的信息 UserDetails,其中包括用户名、密码和所拥有的权限等。所以如果我们需要改变认证的方式,我们可以实现自己的 AuthenticationProvider;如果需要改变认证的用户信息来源,我们可以实现 UserDetailsService
- qq交流群:761778107
spring-Security(二):校验流程(白话版)
最新推荐文章于 2024-07-24 17:09:39 发布