用户登录——⭐认证功能的流程图:
⭐鉴权流程图:
orm框架:JPA
@Table(name = "user_tab")
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="user_id")
private Long id;
@Column(name="user_name")
private String username;
@Column(name="user_password")
private String password;
@Column(name="user_phone")
private String phone;
@Column(name="user_nickname")
private String nickname;
@Column(name="user_create_by")
private String createBy;
@Column(name="user_create_time")
private Date createTime;
@Column(name="user_update_time")
private Date updateTime;
@Column(name="user_role_id")
private Long roleId;
}
@Repository
public interface UserDao extends JpaRepository {
//根据user的username和password查询该用户
User findByUsernameAndPassword(String username,String password);
}
public interface UserService {
//全查询
List findAllUsers();
//1027-【从数据库读取用户名信息存入布隆过滤器中】
void warnUpUsernames();
//1027-【用户登录】
User login(String username, String password);
}
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private StringRedisTemplate stringRedisTemplate;
//全查询
@Override
public List findAllUsers() {
return userDao.findAll();
}
//1027-【从数据库读取用户名信息存入布隆过滤器中】
@Override
public void warnUpUsernames() {
userDao.findAll().forEach(u -> {
stringRedisTemplate.opsForValue()
.getOperations()
.execute(new DefaultRedisScript(
"return redis.call('bf.add',KEYS[1],ARGV[1])"
, Long.class),
new ArrayList() {{
add("whiteUsernames");
}}, u.getUsername()
);
}
);
}
//1027-【用户登录】
@Override
public User login(String username, String password) {
if(!StringUtils.hasText(username)){
throw new UsernameIsEmptyException("用户名为空异常");
}
username = username.trim();
if(checkFromWhite(username)){
throw new UsernameNotFoundException("用户名不存在异常");
}
User user = userDao.findByUsernameAndPassword(username, password);
if(Objects.isNull(user)){
throw new BadCredentialsException("用户名|密码错误");
}
return user;
}
//判断用户名是否存在于布隆过滤器
private boolean checkFromWhite(String username) {
Long isExist = stringRedisTemplate.opsForValue()
.getOperations()
.execute(new DefaultRedisScript("return redis.call('bf.exists',KEYS[1],ARGV[1])", Long.class),
new ArrayList() {{
add("whiteUsernames");
}}, username);
return isExist.intValue() == 0;
}
@Api(tags = "用户模块接口")
@RestController
@RequestMapping("/api/user")
@Slf4j
public class UserController {
@Autowired
private UserService userService;
@Autowired
private StringRedisTemplate stringRedisTemplate;
//1027-【全查询】
@ApiOperation(value = "findAllUsers",notes = "查询所有用户,需要当前用户登录状态")
@GetMapping("/findAllUsers")
@BmsRole(value="1")
public HttpResp> findAllUsers(){
return HttpResp.success(userService.findAllUsers());
}
//1027-【用户登录】
@ApiOperation(value = "login",notes = "用户登录")
@GetMapping("login")
public HttpResp login(HttpServletResponse response, String username, String password){
//首先调用AuthorityService的login方法进行用户登录验证,返回一个User对象。
User user = userService.login(username,password);
//然后生成一个JWT作为用户的身份认证凭证,其中包含了用户名和过期时间等信息。
//使用JWT.create()创建JWT对象,并使用withClaim()方法设置用户名,withExpiresAt()方法设置过期时间。
String salt = Base64.getEncoder().encodeToString((username+":"+password).getBytes(StandardCharsets.UTF_8));
log.debug("user:{}",user);
String token = JWT.create()
.withClaim("username", username)
.withClaim("roleId",""+user.getRoleId())
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 30)) //30分钟令牌过期
.sign(Algorithm.HMAC256(salt)); //使用Algorithm.HMAC256(salt)指定加密算法和密钥,对JWT进行签名。
log.debug("用户验证通过,生成token为:{}",token);
//将生成的JWT存储到Redis缓存中,使用stringRedisTemplate.opsForValue().set()方法设置键值对,并使用stringRedisTemplate.expire()方法设置过期时间
stringRedisTemplate.opsForValue().set(token,salt);
stringRedisTemplate.expire(token,60, TimeUnit.MINUTES); //60分钟redis缓存token过期
response.addCookie(new Cookie("token",token)); //将JWT作为Cookie添加到HTTP响应中,使用response.addCookie()方法
return HttpResp.success(user); //最后返回一个成功的响应,消息体中包含了登录成功的用户对象
}
}
@Configuration
@Slf4j
public class BmsMvcConfig implements WebMvcConfigurer {
@Bean
public AuthorityInterceptor authorityInterceptor(){
log.debug("BmsMvc拦截器启动成功:{}..........",new Date());
return new AuthorityInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authorityInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns(
"/api/user/login"
);
}
}
@Slf4j
public class AuthorityInterceptor implements HandlerInterceptor {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
*
* @param request
* @param response
* @param handler 当前拦截器拦截的方法
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.debug("已进入拦截器:{}",new Date());
String token = request.getHeader("token");
//1.从redis中读取token,如果不存在,则用户是非法用户,抛出自定义异常类InvalidTokenException
Boolean isRedis = stringRedisTemplate.hasKey(token);
if(!isRedis) throw new InvalidTokenException("无效的token");
String salt = stringRedisTemplate.opsForValue().get(token);
log.debug("salt:{}",salt);
DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(salt)).build().verify(token);
//2.token验证完成正确
String roleId = decodedJWT.getClaim("roleId").asString();
log.debug("------->roleId:{}",roleId);
HandlerMethod method = (HandlerMethod) handler;
//System.out.println("----->"+method.getMethod().getDeclaredAnnotation(GetMapping.class));
BmsRole bmsRole= method.getMethod().getDeclaredAnnotation(BmsRole.class);
String requiredRolId = bmsRole.value();
if(!roleId.equals(requiredRolId)){
throw new PermissionDeniedException("您没有足够的权限");
}
//获取请求对象的角色名称
//获取请求的地址(uri)
// String requestURI = request.getRequestURI();
// requestURI = requestURI.substring(requestURI.lastIndexOf("/") + 1);
// log.debug("请求路径uri:{}",requestURI);//URL/URI
// System.out.println(handler.getClass());
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.****.bms.authority.config.BmsMvcConfig,com.****.bms.authority.handler.UserExceptionHandler
未完待续......