shiro学自闭了
先修改登录逻辑,在用户第一次登陆的时候不经过shiro控制,直接走controller然后颁发accestoken
同时还修改了数据库密码逻辑
之前的密码逻辑是存的时候直接在数据库存md5加密好的密码
然后前端发过来的时候发的也是md5加密好的密码,
但是觉得不够安全
就加了盐
前端就发明文过来(虽然看起来好像更加不安全了)//可能以后会再在前端传的密码上再加密,后端再解密
然后后端根据传过来的密码再验证。
用了别人开源项目的密码工具
public class PasswordUtils {
/**
* 匹配密码
* @param salt 盐
* @param rawPass 明文
* @param encPass 密文
* @return
*/
public static boolean matches(String salt, String rawPass, String encPass) {
return new PasswordEncoder(salt).matches(encPass, rawPass);
}
/**
* 明文密码加密
* @param rawPass 明文
* @param salt
* @return
*/
public static String encode(String rawPass, String salt) {
return new PasswordEncoder(salt).encode(rawPass);
}
/**
* 获取加密盐
* @return
*/
public static String getSalt() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 20);
}
}
public class PasswordEncoder {
private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",
"e", "f" };
private final static String MD5 = "MD5";
private final static String SHA = "SHA";
private Object salt;
private String algorithm;
public PasswordEncoder(Object salt) {
this(salt, MD5);
}
public PasswordEncoder(Object salt, String algorithm) {
this.salt = salt;
this.algorithm = algorithm;
}
/**
* 密码加密
* @param rawPass 明文
* @return 密文
*/
public String encode(String rawPass) {
String result = null;
try {
MessageDigest md = MessageDigest.getInstance(algorithm);
// 加密后的字符串
result = byteArrayToHexString(md.digest(mergePasswordAndSalt(rawPass).getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 密码匹配验证
* @param encPass 密文
* @param rawPass 明文
* @return 验证通过返回true
*/
public boolean matches(String encPass, String rawPass) {
String pass1 = "" + encPass;
String pass2 = encode(rawPass);
return pass1.equals(pass2);
}
private String mergePasswordAndSalt(String password) {
if (password == null) {
password = "";
}
if ((salt == null) || "".equals(salt)) {
return password;
} else {
return password + "{" + salt.toString() + "}";
}
}
/**
* 转换字节数组为16进制字串
*
* @param b
* 字节数组
* @return 16进制字串
*/
private String byteArrayToHexString(byte[] b) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
/**
* 将字节转换为16进制
* @param b
* @return
*/
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n = 256 + n;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
}
同时修改了JwtUtils
因为现在只用了颁发token,还没加入校验,先这么写着
public class JwtUtils {
private static final String issuer = "LeeZed";
private static final String secretKey = "FucXingMht"; // token 密钥
private static final Long accessTokenExpireTime = 1000*60*5L;//accessToken (发给客户端的5分钟过期一次)
private static final Long refreshTokenExpireTime = 1000*60*60*24L; //refreshToken(保留再redis的一天过期一次)
/**
* 生成 access_token
*/
public static String getAccessToken(String subject, Map<String,Object> claims){
return generateToken(issuer,subject,claims,accessTokenExpireTime,secretKey);
}
/*
* 生成refreshtoken
* */
public static String getRefreshToken(String subject,Map<String,Object> claims){
return generateToken(issuer,subject,claims,refreshTokenExpireTime,secretKey);
}
/**
* 签发token
*/
public static String generateToken(String issuer, String subject, Map<String, Object> claims, long ttlMillis, String secret) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
byte[] signingKey = DatatypeConverter.parseBase64Binary(secret);
JwtBuilder builder = Jwts.builder();
builder.setHeaderParam("typ","JWT");
if(null!=claims){
builder.setClaims(claims);
}
//if (!StringUtils.isEmpty(subject)) {//这个方法以弃用
if(StringUtils.hasLength(subject)){
builder.setSubject(subject);
}
//if (!StringUtils.isEmpty(issuer)) {
if(StringUtils.hasLength(issuer)){
builder.setIssuer(issuer);
}
builder.setIssuedAt(now);
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
builder.setExpiration(exp);
}
builder.signWith(signatureAlgorithm, signingKey);
return builder.compact();
}
}
登录逻辑
下面的登录逻辑都是转载的
https://www.cnblogs.com/XueTing/p/13732956.html
第一次登录逻辑,完全剥离了shiro
第一次登录直接走controller层
代码
public ResultObj login(String username, String password) {
AdminUser user = adminUserDao.login(username);
int user_id = user.getId();
String user_password = user.getPassword();
//检查密码是否正确
boolean passwordFlag = PasswordUtils.matches(user.getSalt(), password, user_password);
if (user_id == 0 || !passwordFlag) {
return new ResultObj(Constant.ERROR, Constant.USERNAME_PASSWORD_ERROR, null);
}
if (!user.getAvailable()) {
return new ResultObj(Constant.ERROR, Constant.ADMINUSER_NOT_AVAILABLE, null);
}
//到这里就算登录通过,生成accessToken
Map<String, Object> claims = new HashMap<>();
//这里要加权限信息 加载claim中
//留空
claims.put(Constant.JWT_USER_NAME, user.getUsername());
//accessToken 中传入adminuser的id和claims
String accessToken = JwtUtils.getAccessToken(String.valueOf(user_id), claims);
//创建 refreshToken 存入redis
//具体逻辑尚未实现
String refreshToken = JwtUtils.getRefreshToken(String.valueOf(user_id),claims);
//输出登录ip(为功能升级做准备)
String ip = WebUtils.getRequest().getRemoteAddr();
log.info("登录人" + username + "的IP为" + ip);
return new ResultObj(Constant.OK, "登陆成功", accessToken);
}
前端部分
前端实现了导航栏和标签页切换,但是有不少小bug,之后再改
用了别人的table组件,但是还没有按照自己的适配