这就是利用jwt登录的整个基本流程,一般首先重写接口UserDetailService,重写方法loadUserByUsername(String username),改变查询用户的方式,源代码是在内存中查找,我们改为在数据库中查找
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUserName,username);
User user =userMapper.selectOne(queryWrapper);
if(Objects.isNull(user)){
throw new RuntimeException("用户名或者密码错误");
}
//数据返回UserDetails类型,把数据封装成UserDetails类型 但UserDetails是接口,则利用一个UserDetails的实现类,我们自己写一个实现类去实现该接口就行,重写里面的方法
return new LoginUser(user);
}
}
然后我们去就可以正常去写我们的登录接口,也就是上图中的第一步 提交用户名和密码
注意是封装到Authentication对象中
所有我们把密码和用户封装到Authentication接口的实现类中
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());
//然后就可以把该对象放入authenticationManager中,然后就会执行到我们刚刚重写的 UserDetailsServiceImpl
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
以下是登录接口的整体代码
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private RedisCache redisCache; //对redisTemplate各种方法的再一次封装
@Override
public ResponseResult login(User user) {
//AuthenticationManager authenticate 进行用户认证
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
//如果认证没通过,给出对应的提示
if(Objects.isNull(authenticate)){
throw new RuntimeException("登陆失败");
}
//如果认证通过了,使用userid生成一个jwt jwt存入ResponseResult返回
//注意通过getPrincipal取主体,然后记住返回的是经过 重写 UserDetailsServiceImpl 的对象
LoginUser loginUser =(LoginUser)authenticate.getPrincipal();
String userid = loginUser.getUser().getId().toString();
String jwt = JwtUtil.createJWT(userid); //通过jwt工具类生成的token
HashMap<String, String> map = new HashMap<>();
map.put("token",jwt);
//把完整的用户信息存入redis userid作为key
redisCache.setCacheObject("login:"+userid,loginUser);
return new ResponseResult(200,"登陆成功",map);
}
}```
忘了很重要的springsecurityConfig,这个应该是最先写的,登录接口自动注入的 **authenticationManager**就是来自这里重写 **authenticationManager()**返回出去的
```java
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//创建BCyptPasswordEncoder并注入容器,就这样后面关于密码的验证就可以使用该格式加密
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//SecurityConfig中一定要设置configure,其他基本是固定的,关闭跨域呀,关闭session呀,主要就是白名单会不一样
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//关闭csrf
.csrf().disable()
//不通过Session获取SecurityContext
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 对于登录接口 允许匿名访问
.antMatchers("/user/login").anonymous()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
}
@Override
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}