前后端分离中使用基于jwt的token进行身份认证
基于jwt的Token认证机制可以看之前的文章:
基于JWT的Token认证机制实现
在前后端分离中,我们与前端约定一种身份认证机制。当用户登录的时候,我们会返回给前端一个token,前端会将token拿到并按照一定规则放到header中在下一次请求中发送给后端,后端进行token身份校验。
这里我们约定前端请求后端服务时需要添加头信息Authorization ,内容为Test:+空格+token
1.导入pom依赖
<dependencies><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.49</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.16</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.6.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
2.jwt工具类
@ConfigurationProperties("jwt.config")
@Component
@Data
public class JwtUtils {/*** 签名私钥*/private String key;/*** 签名的失效时间*/private Long ttl;
/*** 设置认证token* id:登录用户id* subject:登录用户名*/public String createJwt(String id, String name, Map<String, Object> map) {//1.设置失效时间long now = System.currentTimeMillis();long exp = now + ttl;//2.创建jwtBuilderJwtBuilder jwtBuilder = Jwts.builder().setId(id).setSubject(name).setIssuedAt(new Date()).signWith(SignatureAlgorithm.HS256, key);//3.根据map设置claimsfor (Map.Entry<String, Object> entry : map.entrySet()) {jwtBuilder.claim(entry.getKey(), entry.getValue());}jwtBuilder.setExpiration(new Date(exp));//4.创建tokenreturn jwtBuilder.compact();}
/*** 解析token字符串获取clamis*/public Claims parseJwt(String token) {try {return Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();} catch (Exception e) {return null;}}
}
3.Spring的yml配置
jwt:config:key: testttl: 360000
server:port: 10001
4.全局常量类
public class GlobalConstant {public static final String AUTHORIZATION = "Authorization";public static final String USER_KEY = "user_key";
}
5.通用返回类
前后端分离中,通常会与前端约定一个默认的返回格式,并定义一些不同场景下的状态码。
Result.java
@Data
@NoArgsConstructor
public class Result {
private boolean success;//是否成功private Integer code;// 返回码private String message;//返回信息private Object data;// 返回数据
public Result(ResultCode code) {this.success = code.success;this.code = code.code;this.message = code.message;}
public Result(ResultCode code, Object data) {this.success = code.success;this.code = code.code;this.message = code.message;this.data = data;}
public Result(Integer code, String message, boolean success) {this.code = code;this.message = message;this.success = success;}
public static Result SUCCESS(){return new Result(ResultCode.SUCCESS);}
public static Result ERROR(){return new Result(ResultCode.SERVER_ERROR);}
public static Result FAIL(){return new Result(ResultCode.FAIL);}
}
ResultCode.java
public enum ResultCode {
SUCCESS(true,10000,"操作成功!"),//---系统错误返回码-----FAIL(false,10001,"操作失败"),UNAUTHENTICATED(false,10002,"您还未登录"),UNAUTHORISE(false,10003,"权限不足"),SERVER_ERROR(false,99999,"抱歉,系统繁忙,请稍后重试!"),
//---用户操作返回码 2xxxx----MOBILEORPASSWORDERROR(false,20001,"用户名或密码错误");
//---企业操作返回码 3xxxx----//---权限操作返回码----//---其他操作返回码----
//操作是否成功boolean success;//操作代码int code;//提示信息String message;
ResultCode(boolean success,int code, String message){this.success = success;this.code = code;this.message = message;}
public boolean success() {return success;}
public int code() {return code;}
public String message() {return message;}
}
6.拦截器注解
在我们后端的接口中,并不是所有接口都需要拦截,比如登录接口就不需要登录就能访问,比如有的接口有游客模式也不需要登录访问。我们自定义一个注解,然后在接下来的全局拦截器中去特殊处理这些不需要拦截的接口。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExcludeInterceptor {boolean value() default true;
}
7.拦截器
利用Spring的Intercepter拦截器,拦截所有请求,去校验用户是否登录。同时将用户的id获取到,设置到session中,方便代码直接获取使用。
注意:为了防止拦截器二次拦截,需要配置不拦截/error请求
@Configuration
public class WebMvcConfiguration extends WebMvcConfigurationSupport {@Autowiredprivate JwtUtils jwtUtils;private Logger logger = LoggerFactory.getLogger(this.getClass());
protected static Collection<HandlerMethodArgumentResolver> methodArgumentResolverList;
@Overrideprotected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {super.addArgumentResolvers(argumentResolvers);if (methodArgumentResolverList != null) {argumentResolvers.addAll(methodArgumentResolverList);}}
@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH").allowCredentials(true).maxAge(3600);}
/*** FastJson配置*/@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {converters.add(fastJsonHttpMessageConverters());}
@Beanpublic HttpMessageConverter fastJsonHttpMessageConverters() {//1. 需要定义一个converter转换消息的对象FastJsonHttpMessageConverter fasHttpMessageConverter = new FastJsonHttpMessageConverter();//2. 添加fastjson的配置信息,比如:是否需要格式化返回的json的数据FastJsonConfig fastJsonConfig = new FastJsonConfig();//3. 处理中文乱码问题List<MediaType> fastMediaTypes = new ArrayList<>();fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);fasHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);//4. 在converter中添加配置信息fasHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
return fasHttpMessageConverter;}
@Beanpublic SecurityInterceptor getSecurityInterceptor() {return new SecurityInterceptor();}
@Overridepublic void addInterceptors(InterceptorRegistry registry) {InterceptorRegistration interceptor = registry.addInterceptor(getSecurityInterceptor());ArrayList<String> list = new ArrayList<>();list.add("/error");list.add("/static/**");interceptor.excludePathPatterns(list);super.addInterceptors(registry);}
protected class SecurityInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println(request.getMethod());if ("OPTIONS".equals(request.getMethod())) {if (response != null) {response.setStatus(200);response.sendError(200, "通过");}return false;}
HandlerMethod handlerMethod = (HandlerMethod) handler;ExcludeInterceptor excludeInterceptor = handlerMethod.getMethodAnnotation(ExcludeInterceptor.class);if (excludeInterceptor == null || !excludeInterceptor.value()) {if (request.getHeader(GlobalConstant.AUTHORIZATION) == null) {response.setStatus(401);response.sendError(401, "登录失效,请重新登录");return false;} else {String authorization = request.getHeader(GlobalConstant.AUTHORIZATION);String token = authorization.replace("Test: ", "");Claims claims = jwtUtils.parseJwt(token);if (claims == null) {response.setStatus(401);response.sendError(401, "登录失效,请重新登录");return false;}String userId = claims.getId();request.getSession().setAttribute(GlobalConstant.USER_KEY, userId);
}}return true;}
}
}
8.测试controller
@RestController
public class LoginController {@Autowiredprivate JwtUtils jwtUtils;
@PostMapping("/login")@ResponseBody@ExcludeInterceptorpublic Result login(@RequestBody Map<String, String> loginMap) {String mobile = loginMap.get("username");String password = loginMap.get("password");//登录失败if (mobile == null || !password.equals("123456")) {return new Result(ResultCode.MOBILEORPASSWORDERROR);}//登录成功String token = jwtUtils.createJwt("1", "zhangsan", new HashMap<>());return new Result(ResultCode.SUCCESS, token);}
@GetMapping("/info")@ResponseBodypublic Result getUserInfo(@SessionAttribute(GlobalConstant.USER_KEY) String userId) {//模拟获取该用户的信息HashMap<Object, Object> map = new HashMap<>();map.put("username", "admin");return new Result(ResultCode.SUCCESS, map);}
}
9.测试
启动项目,用postman去访问该项目的登录接口,模拟用户登录
登录成功,返回token
{"code": 10000,"data": "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxIiwic3ViIjoiemhhbmdzYW4iLCJpYXQiOjE1Njg3ODc5NzUsImV4cCI6MTU2ODc4ODMzNX0.8tAvJgytFIKk2wwXRAsJ3IQV51SxnzXCFmmUcAPdbUI","message": "操作成功!","success": true }
登录成功后,将返回的token设置到header中,去访问其它接口
如果Authorization值被伪造或过期:
前后端分离中使用基于jwt的token进行身份认证相关推荐
- 前后端分离中的权限管理思路
在传统的前后端不分的开发中,权限管理主要通过过滤器或者拦截器来进行(权限管理框架本身也是通过过滤器来实现功能),如果用户不具备某一个角色或者某一个权限,则无法访问某一个页面. 但是在前后端分离中,页面 ...
- boke | 前后端分离中使用JWT保持前端数据的持久化,并自动登录
在boke后台登录系统中实现了自动登录的功能,这也是前后端分离的开发模式中最常见的一个问题,如何保持登录状态的持久化.今天就来通过实现自动登录来一步步理清前端数据持久化的思路. 一 实现思路 当用户首 ...
- 单realm模式下前后端分离实现springboot+shiro+jwt+vue整合
shiro+jwt实现前后端分离 一.RBAC概念 基于角色的权限访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注.在R ...
- 前后端分离中后端常用的响应类
在前后端分离开发过程中,后端一般会将数据集封装成一个JSON对象响应给前端 package cn.wideth.response;import lombok.AllArgsConstructor; i ...
- 关于大型网站技术演进的思考(十五)--网站静态化处理—前后端分离—中(7)
出处:夏天的森林博客 上篇里我讲到了一种前后端分离方案,这套方案放到服务端开发人员面前比放在web前端开发人员面前或许得到的掌声会更多,我想很多资深前端工程师看到这样的技术方案可能会有种说不出来的矛盾 ...
- 关于大型网站技术演进的思考(十五)--网站静态化处理—前后端分离—中(7)...
2019独角兽企业重金招聘Python工程师标准>>> 出处:夏天的森林博客 上篇里我讲到了一种前后端分离方案,这套方案放到服务端开发人员面前比放在web前端开发人员面前或许得到的掌 ...
- 解决前后端分离中文件传输跨域失败问题
笔者在之前的开发中就遇到过这种情况:给与后端的文件下载接口,我在浏览器在postman上试过都是可以的,但在前端上就报这种错误: 看上面的介绍是说前后端跨域问题,可是后端代码中明明配置了放行所有请求, ...
- 基于 JWT + Refresh Token 的用户认证实践
HTTP 是一个无状态的协议,一次请求结束后,下次在发送服务器就不知道这个请求是谁发来的了(同一个 IP 不代表同一个用户),在 Web 应用中,用户的认证和鉴权是非常重要的一环,实践中有多种可用方案 ...
- 从壹开始前后端分离 40 || 完美基于AOP的接口性能分析
旁白音:本文是不定时更新的.net core,当前主线任务的Nuxt+VueAdmin教程的 nuxt.js 之 tibug项目已上线,大家可以玩一玩:http://123.206.33.109:70 ...
最新文章
- C#模糊查询绑定datagridview
- IPv6 — 协议头
- 试图将驱动程序添加到存储区_Ubuntu 19.10IOS将捆绑NVIDIA驱动
- 【Arduino】按键按下执行不同模式程序
- 数学建模学习笔记——层次分析法(评价类,专家打分一般不用)
- 12c 新特性之单表恢复
- 学习日报 1028 分支结构 if分支语句
- 上机演练 幸运抽奖活动
- groovy 慢_grails – Groovy中的sql.rows()运行缓慢
- 忘记密码怎么卸载和删除网维大师~
- Python之遍历文件夹图片并重命名
- 关于TensorFlow、DeepDream从开始了解到放弃
- html中如何改变鼠标样式,HTML中常用鼠标样式
- mysql migration toolkit报错_MYSQL Migration Toolkit 安装
- 沧小海读《图解TCP/IP》笔记——第一章	网络基础知识
- 剪辑手法中过肩拍摄的镜头怎么称呼?
- 根据地址获取经度和纬度
- 【图像去噪】基于matlab小波变换(硬阙值+软阙值)图像去噪【含Matlab源码 391期】
- 7. Lock 同步锁
- scrapy框架讲解