单点登录(Single Sign On):简称为 SSO,SSO的定义是在多个模块系统中,用户只需要登录一次就可以访问所有相互信任的系统。有三种实现方法:1、session广播 :一次登录,将session信息复制到其他模块系统中去 2、session和redis实现:将用户信息存到redis中的value中,生成key,再将key存入session中,登录时通过key去redis查询是否存在用户信息 3、token实现
token:实现身份认证的临时令牌,使用某种规则自动生成,官方提供的JWT工具生成一般由三部分构成:1、jwt头信息 2、有效载荷-包含主体信息(用户的id等) 3、签名哈希(防伪标志)
JWT(json web token)
io.jsonwebtoken
jjwt
JWT配置类
/**
*
* JWT工具配置类
*
*/
public class JwtUtils {
public static final long EXPIRE = 1000 * 60 * 60 * 24;
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
public static String getJwtToken(String id, String nickname){
String JwtToken = Jwts.builder()
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
.setSubject("guli-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.claim("id", id)
.claim("nickname", nickname)
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}
/**
* 判断token是否存在与有效
* @param jwtToken
* @return
*/
public static boolean checkToken(String jwtToken) {
if(StringUtils.isEmpty(jwtToken)) return false;
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 判断token是否存在与有效
* @param request
* @return
*/
public static boolean checkToken(HttpServletRequest request) {
try {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return false;
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据token获取会员id
* @param request
* @return
*/
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return "";
Jws claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
return (String)claims.get("id");
}
}
我们肯定是需要一个用户表的,根据这个用户表使用代码生成器逆向生成我们的模块目录,随后修改配置创建启动类我们暂且不表,我们首先在controller中编写出登录功能,随后在service中再详细实现我们的功能
@PostMapping("login")
public R login(@RequestBody LoginVo loginVo) {
String token = memberService.login(loginVo);
return R.success().data("token",token);
}
我实现类中我们主打的就是数据的校验,这里包括是否为空,是否注册,密码是否一致,账号是否禁用,最后才能够通过校验登录成功,具体实现也很简单,就是几个if,也可以用断言完成。
//登录功能接口
@Override
public String login(LoginVo loginVo) {
// 判断登录信息账号密码是否为空
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
throw new LeaderException(20001, "账号或密码为空,登录失败");
}
// 查询用户是否注册
UcenterMember member = baseMapper.selectOne(new QueryWrapper().eq("mobile", mobile));
if (null == member) {
throw new LeaderException(20001, "该手机号码尚未注册");
}
// 判断用户账号密码是否对应
if (!MD5.encrypt(password).equals(member.getPassword())) {
throw new LeaderException(20001, "密码出错,登陆失败");
}
// 检查改账号是否被禁用
if(member.getIsDisabled()) {
throw new LeaderException(20001, "该账户已被禁用,登录失败");
}
// 通过校验 登录成功 使用jwt工具返回token
String jwtToken = JwtUtils.getJwtToken(member.getId(), member.getNickname());
return jwtToken;
}
看上去没什么问题,我们随机启动类,在swagger中去测试我们完成的端口,忘了提一嘴,我们密码使用了MD5加密方式进行加密,所以判断的时候就需要使用加密后的字符串与数据库中保存的加密后的字符串对比。我们最后会使用JWT工具类根据我们的id和昵称进行token的生成,token对我们实现单点登录意义重大。
可以看到我们的token也是成功的返回了。
注册接口和登录差不多,我们同样写一个提交的注册信息vo,在实现类拿到vo提交上来的各注册信息,然后进行判断是否为空,验证码是否正确