文章目录
LoginHandler(将登陆成功的token存入响应头发给前端)
@Controller
@RequestMapping("login")
public class LoginHandler {
@Resource
private LoginService loginService;
/**
* 获取登录验证码
*
* @param phone 手机号
* @return
*/
@GetMapping("valCode")
public ResponseEntity getValidateCode(String phone) {
String valCode = loginService.getValCode(phone);
return ResponseEntity.ok(new ResponseBean(StatusEnum.OPE_SUC, valCode));
}
/**
* 通过手机号和验证码验证登录
*
* @return
*/
@PostMapping("doLogin")
public ResponseEntity doLogin(@RequestBody Map<String, String> map, HttpSession session) {
String phone = map.get("phone");
String valCode = map.get("valCode");
Myuser myuser = loginService.doLogin(phone, valCode);
if (myuser == null) {
throw new MyExceptin(StatusEnum.OPE_ERR);
}
// session.setAttribute("LOGIN_USER", myuser);
// return ResponseEntity.ok(new ResponseBean(StatusEnum.LOGIN_SUC, myuser));
// 登陆成功,生成token字符串
Map<String, Object> token = new HashMap<>();
token.put("phone", phone);
String generate = JwtUtil.generate(token);
// 将token信息放在响应头中发给客户端
myuser.setUpass("");
HttpHeaders headers = new HttpHeaders();
headers.set("token", generate);
return new ResponseEntity(new ResponseBean(StatusEnum.LOGIN_SUC, myuser), headers, HttpStatus.OK);
}
// 注销操作
@GetMapping("doLogout")
public ResponseEntity diLogout(HttpServletRequest request) {
String token = request.getHeader("token");
loginService.doLogout(token);
return ResponseEntity.ok(new StatusBean(StatusEnum.OPE_SUC));
}
}
LoginServiceImpl(登录与注销操作与redis交互)
@Service
public class LoginServiceImpl implements LoginService {
private Logger logger = LoggerFactory.getLogger(LoginServiceImpl.class);
@Resource
private ShardedJedisPool jedisPool;
@Resource
private LoginMapper loginMapper;
/**
* 获取验证码
*
* @param phone 手机号
* @return
*/
@Override
public String getValCode(String phone) {
if (phone == null) {
throw new MyExceptin(StatusEnum.PHONE_EMPTY);
}
String valCode = StringRandom.getValCode();
// 打印日志
logger.info("获取到了验证码,验证码为:" + valCode);
ShardedJedis resource = jedisPool.getResource();
// 5分钟自动过期
resource.setex(RedisHeadInfo.CODE_HEAD + phone, 30 * 60, valCode);
resource.close();
return valCode;
}
/**
* 验证登录信息,返回用户信息
*
* @param phone 登录手机号
* @param valCode 登录验证码
* @return
*/
@Override
public Myuser doLogin(String phone, String valCode) {
if (phone == null) {
throw new MyExceptin(StatusEnum.PHONE_EMPTY);
}
ShardedJedis resource = jedisPool.getResource();
// 查看登录黑名单是否有此对象,有即删除
if (resource.hexists(RedisHeadInfo.LOGOUT_KEY, phone)) {
logger.info("黑名单中有此用户数据,此次操作为重新登陆,将此用户信息移除黑名单");
resource.hdel(RedisHeadInfo.LOGOUT_KEY, phone);
};
resource.close();
// 验证码验证
boolean checked = checkValidateCode(phone, valCode);
// 验证不通过
if (!checked) {
throw new MyExceptin(StatusEnum.OPE_ERR);
}
// 验证通过
return getMyuserByPhone(phone);
}
/**
* 进行验证码校验
*
* @param phone
* @param valCode
* @return
*/
private boolean checkValidateCode(String phone, String valCode) {
ShardedJedis resource = jedisPool.getResource();
String code = resource.get(RedisHeadInfo.CODE_HEAD + phone);
if (code == null || valCode == null || !code.equals(valCode.trim())) {
logger.info("验证码为空或验证码输入错误,操作账户为:" + phone + ",正确验证码为:" + code + ",用户输入的验证码为:" + valCode);
resource.close();
return false;
}
resource.close();
return true;
}
/**
* 检测是否是已注册用户。
* 是的话,合法账户登录。执行登录逻辑
* 否的话,注册新用户,并加标志表示这是一个新用户,执行登录逻辑,返回
*
* @param phone
* @return
*/
public Myuser getMyuserByPhone(String phone) {
ShardedJedis resource = jedisPool.getResource();
// 查看黑名单中是否有此用户数据,有即说明该用户已登出,此次访问为恶意访问
if (resource.hexists(RedisHeadInfo.LOGOUT_KEY, phone)){
resource.close();
logger.info("用户已登出,存在非法访问");
throw new MyExceptin(StatusEnum.USER_LOGOUT);
}
// 先查缓存中是否存有用户信息
String userStr = resource.hget(RedisHeadInfo.LOGIN_KEY, phone);
if (userStr != null) {
logger.info("缓存中存有该用户信息");
resource.close();
return JSON.parseObject(userStr, Myuser.class);
}
// 缓存中没有用户信息,去数据库查找
Myuser myuser = loginMapper.selectByPhone(phone);
if (myuser == null) { // 说明是新用户,新插入一条数据,以默认值自动注册
logger.info("数据库无此用户信息,自动注册");
myuser = new Myuser();
myuser.setUname("游客");
myuser.setUpass("1234");
myuser.setUphone(phone);
loginMapper.insertMyuser(myuser);
myuser.setNewuser("yes"); // 非数据库字段
}
// 将用户的登录信息存入redis
resource.hset(RedisHeadInfo.LOGIN_KEY, phone, JSON.toJSONString(myuser));
logger.info("将用户的登录信息存入了redis");
resource.close();
return myuser;
}
/**
* 注销操作
*
* @param token
* @return
*/
@Override
public void doLogout(String token) {
Claims claim = JwtUtil.getClaim(token);
Object phone = claim.get("phone");
ShardedJedis resource = jedisPool.getResource();
resource.hset(RedisHeadInfo.LOGOUT_KEY, (String) phone, "1");
logger.info("将注销用户的信息存入了redis黑名单");
resource.close();
}
}
前端axios请求与响应拦截器写法
axios.defaults.baseURL = 'http://localhost:8080/0807/';
// axios.defaults.withCredentials = true; //跨域配置
//配置发送请求前的拦截器 可以设置token信息
axios.interceptors.request.use(
config => {
// debugger;
let token = localStorage.getItem("token");
let requestUrl = config.url;
if (requestUrl != "login/doLogin" && requestUrl.indexOf("login/valCode") != 0) {
if (token != null) {
// 向请求头加入token信息
config.headers.token = token;
}else{
// 未登录,转到登录页面
location.href = "http://127.0.0.1:8848/ssm/login.html";
}
}
return config
}, error => {
return Promise.reject(error)
}
)
// 配置响应拦截器
axios.interceptors.response.use(
res => {
// if(res.data.code == 50001){
// // 说明用户未登录
// location.href = "http://127.0.0.1:8848/ssm/login.html";
// }
// debugger;
if(res.data.data == 50002 || res.data.data == 50003){ // token过期失效或者用户已登出的非法访问
location.href = "http://127.0.0.1:8848/ssm/login.html";
}
// 将登陆成功后的token存入localSorage
if ("token" in res.headers) {
//
let token = res.headers.token;
localStorage.setItem("token", token);
}
return Promise.resolve(res.data) // 这里直接返回data, 即接口返回的所有数据
},
error => {
return Promise.reject(error);
}
)
前端页面登出
islogout() {
this.$confirm('此操作将退出系统, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
axios.get('login/doLogout')
.then(response => {
console.log(response);
if (response.code == 20000) {
localStorage.removeItem("token");
location.href = "http://127.0.0.1:8848/ssm/login.html";
}
}).catch(function(error) {
console.log(error);
});
// location.href = "/secondStage/login?method=logout";
});
}
LoginInterceptor(spring注册登录拦截器)
跨域设置开启暴露响应头设置
<mvc:cors>
<mvc:mapping path="/**" exposed-headers="token" allow-credentials="true"/>
</mvc:cors>
配置登陆拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/login/doLogin"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/login/valCode"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/login/doLogout"></mvc:exclude-mapping>
<bean class="com.javasm.common.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private CurrentLoginUser loginUser;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 对预检请求放行
String method = request.getMethod();
if (method.equals("OPTIONS")) {
return true;
}
String token = request.getHeader("token");
if (token != null) {
Claims claim = JwtUtil.getClaim(token);
if(claim!=null){
// 可以设置当前登录用户信息,共享至其他接口
String phone = (String)claim.get("phone");
loginUser.setLoginUser(phone);
//刷新token
String s = refreshToken(claim);
if(s!=null){
response.addHeader("token",s);
}
return true;
}else{
//token已过期
throw new MyExceptin(StatusEnum.TOKEN_EXPIR);
}
} else {
throw new MyExceptin(StatusEnum.OPE_ERR);
}
}
private String refreshToken(Claims claim) {
Date nowDate = new Date();
Date expiration = claim.getExpiration();
//如果剩余有效时间仅剩3分钟的话,刷新token
if ((expiration.getTime() - nowDate.getTime()) <= 3 * 60 * 1000) {
//要刷新token
Object phone = claim.get("phone");
Map<String, Object> map = new HashMap<>();
map.put("phone", phone);
String token = JwtUtil.generate(map);
return token;
}
return null;
}
}
CurrentLoginUser(共享当前登录用户信息)
- 将登录用户信息放入threadLocal维护,使得其他接口也能获得当前登录的用户信息
@Component
public class CurrentLoginUser {
@Resource
private LoginService loginService;
private static final ThreadLocal<Myuser> MYUSER_THREAD_LOCAL = new ThreadLocal<>();
// 通过手机号查询已登录用户信息
public Myuser getLoginUser(){
return MYUSER_THREAD_LOCAL.get();
}
public void setLoginUser(String phone){
Myuser loginUser = loginService.getMyuserByPhone(phone);
MYUSER_THREAD_LOCAL.set(loginUser);
}
}
JwtUtil(生成与解析token)
- 依赖jjwt-0.9.1.jar
public class JwtUtil {
public static final String UID = "uid";
private static final String SECRET = "6A50A18D70FA63636645C65459F1D78A";
private static final long EXPIRE = 5 * 60 * 1000;//有效时间,5分钟
/**
* 生成token
*
* @param claims:字符串中要保存的用户信息
* @return
*/
public static String generate(Map<String, Object> claims) {
Date nowDate = new Date();
//过期时间
Date expireDate = new Date(nowDate.getTime() + EXPIRE);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(nowDate)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
/**
* 解析token
*
* @param token
* @return
*/
public static Claims getClaim(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
return claims;
}
}