1.基于cookie的简单登录校验
cookie鉴权在本文没有做token的加密,通过token/用户信息json键值对的方式存入redis,这样避免了session共享的问题,由于本文实现上省略了token加密,不能从cookie里解密拿到用户信息,再加上cookie有被伪造的风险,所以安全性较低.
有token加密的实现在本文JWT里有做
1.1 用户登录controler
@ApiOperation(value = "用户登录", notes = "登录接口")
@PostMapping("/login")
public ApiResult login(@RequestBody Users uss, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
ApiResult apiResult = new ApiResult<>();
Users us = new Users();
us.setLoginname(uss.getLoginname());
us.setPassword(uss.getPassword());
Users userss = iUsersService.isUser(uss);
if (userss != null) {
String key = UUID.randomUUID().toString().substring(0, 16);
LOG.info("往redis里塞值key:{},value:{}", key, userss.toString());
ApiResult result = redisFeignClient.set(key, JSON.toJSONString(userss), MainConstants.TOKEN_LIFETIME);
if (result.getCode() == CommonCode.SUCCESS.getKey()) {
Cookie cookie = new Cookie(MainConstants.COOKIE_NAME, key);
cookie.setMaxAge(MainConstants.COOKIE_LIFETIME);
cookie.setPath("/");
cookie.setDomain("");
// cookie.setHttpOnly(true);
httpServletResponse.addCookie(cookie);
} else {
apiResult.setMsg(CommonCode.ERROR.getValue());
apiResult.setCode(CommonCode.ERROR.getKey());
apiResult.setMsg("redis服务错误,请联系管理员");
return apiResult;
}
apiResult.setData(userss);
return apiResult;
}
apiResult.setMsg(CommonCode.ERROR.getValue());
apiResult.setCode(CommonCode.ERROR.getKey());
apiResult.setMsg("错误");
return apiResult;
}
1.2 cookie拦截器
public class TokenIntecptor implements HandlerInterceptor {
// redis微服务
private RedisFeignClient redisFeignClient;
public TokenIntecptor(RedisFeignClient redisFeignClient) {
this.redisFeignClient = redisFeignClient;
}
protected static Logger Log = LoggerFactory.getLogger(TokenIntecptor.class);
/**
* 处理前
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 设置跨域
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "content-type");
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=utf-8");
if (null != request.getCookies()) {
// System.out.println("cookie长度" + request.getCookies().length);
String token = null;
for (Cookie cookie : request.getCookies()
) {
// System.out.println("cookie name" + cookie.getName() + "cookie value" + cookie.getValue());
if (MainConstants.COOKIE_NAME.equals(cookie.getName())) {
token = cookie.getValue();
}
}
// System.out.println("token是" + token);
if (token != null) {
ApiResult apiResult = redisFeignClient.get(token);
// 判断redis返回是否能查到数据
if (apiResult.getCode() == CommonCode.SUCCESS.getKey()) {
// 重新设置过期时间
redisFeignClient.getandsetexpire(token, MainConstants.TOKEN_LIFETIME);
// 将user塞入threadlocal中,方便线程获取user对象
String user = apiResult.getData().toString();
RequestHolder.add(JSON.parseObject(user, Users.class));
return true;
} else {
// System.out.println("cookie过期,重新登录");
PrintWriter printWriter = response.getWriter();
ApiResult result = new ApiResult<>();
result.setCode(CommonCode.TOKEN_INVALID.getKey());
result.setMsg("请重新登录");
printWriter.write(result.toString());
return false;
}
}
} else {
PrintWriter printWriter = response.getWriter();
// System.out.println("未携带cookie,重新登录");
ApiResult result = new ApiResult<>();
result.setCode(CommonCode.TOKEN_INVALID.getKey());
result.setMsg("请重新登录");
printWriter.write(result.toString());
return false;
}
PrintWriter printWriter = response.getWriter();
// System.out.println("未携带cookie,重新登录");
ApiResult result = new ApiResult<>();
result.setCode(CommonCode.TOKEN_INVALID.getKey());
result.setMsg("请重新登录");
printWriter.write(result.toString());
return false;
}
/**
* 处理后调用(正常)
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 处理后调用(任何情况)
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 清除threadlocal存储信息,防止内存泄漏
removeThreadLocalInfo();
}
/**
* 移除信息,包括数据源类型,与user信息
*/
public void removeThreadLocalInfo() {
DbContextHolder.clearDbType();
RequestHolder.remove();
}
1.3 cookie拦截器配置类
@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
// 将redisFeignClient作为TokenIntecptor构造方法的传参,避免了由于Spring
// bean加载顺序的原因导致在TokenIntecptor中redisFeignClient注入失败
@Autowired
RedisFeignClient redisFeignClient;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册自定义拦截器,添加拦截路径和排除拦截路径
registry.addInterceptor(new TokenIntecptor(redisFeignClient)).addPathPatterns("/**")
// 排除用户登录
.excludePathPatterns("/user/login")
.excludePathPatterns("/user/logout");
}
1.4 ThreadLocal线程获取对象类
用以方便从线程中拿到对象
public class RequestHolder {
private static final ThreadLocal<Users> userHolder = new ThreadLocal<Users>();
private static final ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal<>();
public static void add(Users sysUser){
userHolder.set(sysUser);
}
public static void add(HttpServletRequest request){
requestHolder.set(request);
}
public static Users getCurrentUser(){
return userHolder.get();
}
public static HttpServletRequest getCurrentRequest(){
return requestHolder.get();
}
public static void remove(){
userHolder.remove();
requestHolder.remove();
}
}
2. JWT鉴权
cookie与jwt都是将认证信息返回给了客户端,最主要的区别是JWT不存在cookie跨域的问题.
2.1 jwt依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
2.2 jwt工具类
@Component
public class JwtUtil {
@Value("${jwt.key}")
private String key;
@Value("${jwt.ttl}")
private long ttl;//一个小时
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public long getTtl() {
return ttl;
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
/**
* 生成JWT
*
* @param id
* @param subject
* @return
*/
public String createJWT(String id, String subject, String roles) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
JwtBuilder builder = Jwts.builder().setId(id)
.setSubject(subject)
.setIssuedAt(now)
// SHA256
.signWith(SignatureAlgorithm.HS256, key).claim("roles",
roles);
if (ttl > 0) {
builder.setExpiration(new Date(nowMillis + ttl));
}
return builder.compact();
}
/**
* 解析JWT
*
* @param jwtStr
* @return
*/
public Claims parseJWT(String jwtStr) {
return Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(jwtStr)
.getBody();
}
}
2.3 用户登录controller
@Autowired
private JwtUtil jwtUtil;
/**
* 用户登陆
*
* @param loginname
* @param password
* @return
*/
@RequestMapping(value = "/login", method = RequestMethod.POST)
public Result login(@RequestBody Map<String, String> loginMap) {
Admin admin =
adminService.findByLoginnameAndPassword(loginMap.get("loginname"),
loginMap.get("password"));
if (admin != null) {
//生成token
String token = jwtUtil.createJWT(admin.getId(),
admin.getLoginname(), "admin");
Map map = new HashMap();
map.put("token", token);
map.put("name", admin.getLoginname());//登陆名
return new Result(true, StatusCode.OK, "登陆成功", map);
} else {
return new Result(false, StatusCode.LOGINERROR, "用户名或密码错
误");
}
}
2.4 token拦截器
@Configuration
public class JwtFilter extends HandlerInterceptorAdapter {
@Autowired
private JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("经过了拦截器");
final String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
final String token = authHeader.substring(7); // The part
after "Bearer "
Claims claims = jwtUtil.parseJWT(token);
if (claims != null) {
if ("admin".equals(claims.get("roles"))) {//如果是管理员
request.setAttribute("admin_claims", claims);
}
if ("user".equals(claims.get("roles"))) {//如果是用户
request.setAttribute("user_claims", claims);
}
}
}
return true;
}
}
2.5 拦截器配置类
@Configuration
public class ApplicationConfig extends WebMvcConfigurationSupport {
@Autowired
private JwtFilter jwtFilter;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtFilter).
addPathPatterns("/**").
excludePathPatterns("/**/login");
}
}