前后端分离中使用基于jwt的token进行身份认证
生活随笔
收集整理的這篇文章主要介紹了
前后端分离中使用基于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: 100014.全局常量類
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进行身份认证的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Activiti与SpringBoot的
- 下一篇: Shiro在SpringBoot中的应用