Html5做旅游网站的设计思路,论坛类型的网站怎么做,手机建设银行官方网站,淘宝电商运营培训背景
我们在基于Session做登录认证的时候#xff0c;会有一些问题#xff0c;因为Session存储到服务器端#xff0c;然后通过客户端的Cookie进行匹配#xff0c;如果正确#xff0c;则通过认证#xff0c;否则不通过认证。这在简单的系统中可以这么使用#xff0c;并且…背景
我们在基于Session做登录认证的时候会有一些问题因为Session存储到服务器端然后通过客户端的Cookie进行匹配如果正确则通过认证否则不通过认证。这在简单的系统中可以这么使用并且难度是最低的但是如果在大型分布式项目中如果还是基于Session做登录认证的话就不可行了。这个时候我们可以基于token做登录认证。token其实就是一个字符串生成token的实现方案有很多种可以使用uuid作为token也可以使用jwt作为token其中使用jwt实现的方案是最流行的那么下面将会讲如何在SpringBoot中基于jwt实现token登录认证。
1. 引入依赖
dependencygroupIdcom.auth0/groupIdartifactIdjava-jwt/artifactIdversion4.4.0/version
/dependency2. 自定义注解
自定义一个注解在需要认证的方法上添加该注解
Target({ElementType.METHOD,ElementType.TYPE})
Retention(RetentionPolicy.RUNTIME)
public interface Auth {boolean require() default true;
}3. 编写拦截器
通过识别是否在接口上添加Auth注解来确定是否需要登录才能访问。
同时这里需要注意只拦截HandlerMethod类型同时还要考虑放行BasicErrorController因为基本的报错在这个控制器中如果不放行那么会看不到报错信息。
public class LoginInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod (HandlerMethod) handler;if (handlerMethod.getBean() instanceof BasicErrorController) {return true;}Auth auth handlerMethod.getMethod().getAnnotation(Auth.class);if (auth ! null auth.require()) {String token request.getHeader(token);if (StringUtils.isNotBlank(token)) {if (TokenUtil.verifyToken(token)) { // 校验 token 是否正确return true;} else {request.getRequestDispatcher(/error/tokenError).forward(request, response); // 这里你也可以直接抛出自定义异常然后在全局异常处理器中处理}} else {request.getRequestDispatcher(/error/token).forward(request, response); // 这里你也可以直接抛出自定义异常然后在全局异常处理器中处理}} else {return true;}} else {return true;}return false;}
}4. 定义跨域拦截器
这里是做前后端分离需要做的步骤解决跨域的方式有好几种这里使用拦截器的方式解决跨域问题。
public class CrossInterceptorHandler implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {response.setHeader(Access-Control-Allow-Origin, *);response.setHeader(Access-Control-Allow-Credentials, true);response.setHeader(Access-Control-Allow-Methods, POST, GET , PUT , OPTIONS);response.setHeader(Access-Control-Max-Age, 3600);response.setHeader(Access-Control-Allow-Headers, x-requested-with,accept,authorization,content-type);return true;}
}5. 定义全局异常处理器
这里没有用到全局异常处理器不过为了项目的完整性我还是选择把这些常规的内容写上去。
RestControllerAdvice
public class GlobalException {public final Logger logger LoggerFactory.getLogger(this.getClass());ExceptionHandler(TokenExpiredException.class)public R? handleTokenExpiredException(TokenExpiredException e) {logger.error(token 已过期);logger.error(e.getMessage());return R.error(ResponseEnum.TOKEN_EX);}
}6. 定义工具类
6.1 统一错误状态码
编写一个枚举类统一项目的报错状态码。
AllArgsConstructor
Getter
public enum ResponseEnum {SUCCESS(200, 操作成功),FAIL(300,获取数据失败),USER_EX(301,用户不存在请重新登录),ERROR(302,错误请求),USERNAME_PASSWORD_ERROR(303,用户名或密码错误),NO_TOKEN(400,无token请重新登录),TOKEN_VERIFY_ERROR(401,token验证失败请重新登录),TOKEN_EX(402,token已过期);private final Integer code;private final String msg;public static ResponseEnum getResultCode(Integer code){for (ResponseEnum value : ResponseEnum.values()) {if (code.equals(value.getCode())){return value;}}return ResponseEnum.ERROR;}
}6.2 统一响应类
Data
public class RT implements Serializable {private static final long serialVersionUID 56665257244236049L;private Integer code;private String message;private T data;private R() {}public static T RT ok(T data) {RT response new R();response.setCode(ResponseEnum.SUCCESS.getCode());response.setMessage(ResponseEnum.SUCCESS.getMsg());response.setData(data);return response;}public static T RT error(Integer errCode, String errMessage) {RT response new R();response.setCode(errCode);response.setMessage(errMessage);return response;}public static T RT error(ResponseEnum responseEnum) {RT response new R();response.setCode(responseEnum.getCode());response.setMessage(responseEnum.getMsg());return response;}
}6.3 Token工具类
通过TokenUtil可以生成token和验证token是否正确。
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;import java.util.Date;/*** author Luke Ewin* date 2024/2/19 16:59* blog blog.lukeewin.top*/
public class TokenUtil {private final static String ENCRYPT_KEY abc123; // 加密的密钥private final static int EXPIRE_TIME 1; // token 过期时间单位分钟private static final String ISSUER zhangsan;/*** 生成 token** param json 要封装到 token 的内容如果要传递多个参数内容可以定义为 JSON 或者 Map* return 返回 token*/public static String createToken(JSONObject json) {return JWT.create().withSubject(json.toString()) // 不要把密码封装进去不安全.withIssuer(ISSUER) // 设置发布者.withExpiresAt(DateUtil.offsetMinute(new Date(), EXPIRE_TIME)) // 设置过期时间.withClaim(test, 123) // 这里是随便设置的内容类似 Map.sign(Algorithm.HMAC256(ENCRYPT_KEY)); // 加密}/*** 验证 token** param token* return*/public static boolean verifyToken(String token) {try {JWTVerifier jwtVerifier JWT.require(Algorithm.HMAC256(ENCRYPT_KEY)).withIssuer(ISSUER).build();jwtVerifier.verify(token);return true;} catch (Exception e) { // 如果 token 过期会报错 TokenExpiredExceptione.printStackTrace();return false;}}
}7. 编写实体类
这里为了简单并没有与数据库交互。
Data
public class User {private String userName;private String password;private String token;
}8. 定义控制器
8.1 定义登录控制器类
RestController
RequestMapping(/user)
public class LoginController {PostMapping(/login)public RUser login(String userName, String password) {if (StringUtils.isNotBlank(userName) StringUtils.isNotBlank(password)) {if (张三.equals(userName) 123456.equals(password)) {User user new User();JSONObject json JSONUtil.createObj().put(name, zhangsan);String token TokenUtil.createToken(json);user.setToken(token);return R.ok(user);}}return R.error(ResponseEnum.USERNAME_PASSWORD_ERROR);}
}8.2 定义报错处理器
RestController
RequestMapping(/error)
public class ErrorController {PostMapping(/token)public R? token() {return R.error(ResponseEnum.NO_TOKEN);}PostMapping(/tokenError)public R? tokenError() {return R.error(ResponseEnum.TOKEN_VERIFY_ERROR);}
}8.3 定义测试控制器
RestController
RequestMapping(/test)
public class TestController {AuthPostMapping(/hello)public R? hello() {return R.ok(登录成功);}PostMapping(/hi)public R? hi() {return R.ok(登录成功);}
}9. 配置类
最后别忘了定义一个配置类把我们自定义的两个拦截器注册进去。
Configuration
public class WebMvcConfig implements WebMvcConfigurer {Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new CrossInterceptorHandler()).addPathPatterns(new String[] {/**});registry.addInterceptor(new LoginInterceptor()).addPathPatterns(/**).excludePathPatterns(/user/login, /error/**);}
}10. 最终的效果
访问登录接口通过提交表单方式提交请求通过token验证后会返回一个token然后我们请求添加了Auth注解的接口都需要在请求头添加token字段和对应的值。 如果请求头中没有填写token或者填写的不对在请求需求登录后才能访问的接口时都会报错。比如这里的/test/hello是需要登录后才能访问的接口如果没有正确填写token那么会报错如下图所示。 如果正确填写了token那么效果如下。 有一个test/hi接口没有Auth注解可以不用登录就能访问如下图所示。 以上就是本篇文章所分享的内容如果对你有用记得收藏哦
更多Java干货欢迎关注我的博客。
代码已经开源到github中如需要下载源代码可点击这里。