文章目录

  • Bearer认证(JWT-Token)- 用户信息存客户端中
    • 讲解(Bearer认证)
    • 实现(Bearer认证)
      • 代码(Bearer认证)
      • 演示(Bearer认证)
        • 浏览器
        • postman


认证大全(想学习其他认证,请到此链接查看)

Bearer认证(JWT-Token)- 用户信息存客户端中

讲解(Bearer认证)

参考文章: https://blog.csdn.net/qq_35642036/article/details/82788588

官网(JWT): https://jwt.io/introduction

官方文章(Bearer认证定义细节 == 必须看 == 英语菜逼得自行谷歌翻译): https://datatracker.ietf.org/doc/html/rfc6750

注意: 登录成功的响应报文中有个基于用户信息生成一个token字段,下一次新请求时每次在请求头都会携带token值过去给服务端,服务端在校验token值是否合法,合法则说明用户有权限访问链接,不合法直接响应报文返回报错或者401

#mermaid-svg-45vauBWrPZ6HUg3n {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-45vauBWrPZ6HUg3n .error-icon{fill:#552222;}#mermaid-svg-45vauBWrPZ6HUg3n .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-45vauBWrPZ6HUg3n .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-45vauBWrPZ6HUg3n .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-45vauBWrPZ6HUg3n .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-45vauBWrPZ6HUg3n .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-45vauBWrPZ6HUg3n .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-45vauBWrPZ6HUg3n .marker{fill:#333333;stroke:#333333;}#mermaid-svg-45vauBWrPZ6HUg3n .marker.cross{stroke:#333333;}#mermaid-svg-45vauBWrPZ6HUg3n svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-45vauBWrPZ6HUg3n .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-45vauBWrPZ6HUg3n .cluster-label text{fill:#333;}#mermaid-svg-45vauBWrPZ6HUg3n .cluster-label span{color:#333;}#mermaid-svg-45vauBWrPZ6HUg3n .label text,#mermaid-svg-45vauBWrPZ6HUg3n span{fill:#333;color:#333;}#mermaid-svg-45vauBWrPZ6HUg3n .node rect,#mermaid-svg-45vauBWrPZ6HUg3n .node circle,#mermaid-svg-45vauBWrPZ6HUg3n .node ellipse,#mermaid-svg-45vauBWrPZ6HUg3n .node polygon,#mermaid-svg-45vauBWrPZ6HUg3n .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-45vauBWrPZ6HUg3n .node .label{text-align:center;}#mermaid-svg-45vauBWrPZ6HUg3n .node.clickable{cursor:pointer;}#mermaid-svg-45vauBWrPZ6HUg3n .arrowheadPath{fill:#333333;}#mermaid-svg-45vauBWrPZ6HUg3n .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-45vauBWrPZ6HUg3n .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-45vauBWrPZ6HUg3n .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-45vauBWrPZ6HUg3n .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-45vauBWrPZ6HUg3n .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-45vauBWrPZ6HUg3n .cluster text{fill:#333;}#mermaid-svg-45vauBWrPZ6HUg3n .cluster span{color:#333;}#mermaid-svg-45vauBWrPZ6HUg3n div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-45vauBWrPZ6HUg3n :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

能控制请求头的情况
不能控制请求头的情况下
不能控制请求头的情况下
三种方式进行发送token给服务器进行认证
1. 请求头:Authorization: Bearer token字符串
2. Get请求:访问地址?access_token=token字符串
3. Post请求且是application/x-www-form-urlencoded == 说明了其实就是表单 post请求commit的发送格式== 设置表单参数access_token=token字符串
#mermaid-svg-Zp7hFGijYuhqUb5m {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Zp7hFGijYuhqUb5m .error-icon{fill:#552222;}#mermaid-svg-Zp7hFGijYuhqUb5m .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Zp7hFGijYuhqUb5m .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Zp7hFGijYuhqUb5m .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Zp7hFGijYuhqUb5m .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Zp7hFGijYuhqUb5m .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Zp7hFGijYuhqUb5m .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Zp7hFGijYuhqUb5m .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Zp7hFGijYuhqUb5m .marker.cross{stroke:#333333;}#mermaid-svg-Zp7hFGijYuhqUb5m svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Zp7hFGijYuhqUb5m .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Zp7hFGijYuhqUb5m .cluster-label text{fill:#333;}#mermaid-svg-Zp7hFGijYuhqUb5m .cluster-label span{color:#333;}#mermaid-svg-Zp7hFGijYuhqUb5m .label text,#mermaid-svg-Zp7hFGijYuhqUb5m span{fill:#333;color:#333;}#mermaid-svg-Zp7hFGijYuhqUb5m .node rect,#mermaid-svg-Zp7hFGijYuhqUb5m .node circle,#mermaid-svg-Zp7hFGijYuhqUb5m .node ellipse,#mermaid-svg-Zp7hFGijYuhqUb5m .node polygon,#mermaid-svg-Zp7hFGijYuhqUb5m .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Zp7hFGijYuhqUb5m .node .label{text-align:center;}#mermaid-svg-Zp7hFGijYuhqUb5m .node.clickable{cursor:pointer;}#mermaid-svg-Zp7hFGijYuhqUb5m .arrowheadPath{fill:#333333;}#mermaid-svg-Zp7hFGijYuhqUb5m .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Zp7hFGijYuhqUb5m .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Zp7hFGijYuhqUb5m .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Zp7hFGijYuhqUb5m .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Zp7hFGijYuhqUb5m .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Zp7hFGijYuhqUb5m .cluster text{fill:#333;}#mermaid-svg-Zp7hFGijYuhqUb5m .cluster span{color:#333;}#mermaid-svg-Zp7hFGijYuhqUb5m div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Zp7hFGijYuhqUb5m :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

选填
demo1
demo2
响应码400
响应码401
响应码402
认证失败响应头格式:WWW-Authenticate: Bearer 认证其他信息
realm:需要什么角色权限的token说明描述
scope:需要什么角色范围的token才能访问到当前资源
scope=openid profile email
scope=urn:example:channel=HBO&urn:example:rating=G,PG-13
error:认证失败的状态码
invalid_request:请求缺失必要的参数,如没传token信息过来
invalid_token:token不合法,认证失效
insufficient_scope:当前token合法,但是没权限访问当前URL
error_description:认证失败原因,用人话翻译一遍error的具体状态描述
#mermaid-svg-ZoVAju6qdF0cFWc6 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ZoVAju6qdF0cFWc6 .error-icon{fill:#552222;}#mermaid-svg-ZoVAju6qdF0cFWc6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZoVAju6qdF0cFWc6 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-ZoVAju6qdF0cFWc6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZoVAju6qdF0cFWc6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZoVAju6qdF0cFWc6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZoVAju6qdF0cFWc6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZoVAju6qdF0cFWc6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZoVAju6qdF0cFWc6 .marker.cross{stroke:#333333;}#mermaid-svg-ZoVAju6qdF0cFWc6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZoVAju6qdF0cFWc6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZoVAju6qdF0cFWc6 .cluster-label text{fill:#333;}#mermaid-svg-ZoVAju6qdF0cFWc6 .cluster-label span{color:#333;}#mermaid-svg-ZoVAju6qdF0cFWc6 .label text,#mermaid-svg-ZoVAju6qdF0cFWc6 span{fill:#333;color:#333;}#mermaid-svg-ZoVAju6qdF0cFWc6 .node rect,#mermaid-svg-ZoVAju6qdF0cFWc6 .node circle,#mermaid-svg-ZoVAju6qdF0cFWc6 .node ellipse,#mermaid-svg-ZoVAju6qdF0cFWc6 .node polygon,#mermaid-svg-ZoVAju6qdF0cFWc6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZoVAju6qdF0cFWc6 .node .label{text-align:center;}#mermaid-svg-ZoVAju6qdF0cFWc6 .node.clickable{cursor:pointer;}#mermaid-svg-ZoVAju6qdF0cFWc6 .arrowheadPath{fill:#333333;}#mermaid-svg-ZoVAju6qdF0cFWc6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZoVAju6qdF0cFWc6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZoVAju6qdF0cFWc6 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-ZoVAju6qdF0cFWc6 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-ZoVAju6qdF0cFWc6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZoVAju6qdF0cFWc6 .cluster text{fill:#333;}#mermaid-svg-ZoVAju6qdF0cFWc6 .cluster span{color:#333;}#mermaid-svg-ZoVAju6qdF0cFWc6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZoVAju6qdF0cFWc6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

浏览器不得缓存结果
浏览器不得缓存结果
授权成功获取Token的响应请求头设置
Cache-Control:no-store
Pragma:no-cache

官方:Bearer认证失败响应案例(400、401、402)

官方:授权成功(登录成功)后获取token的响应案例

实现(Bearer认证)

代码(Bearer认证)

文件结构


application.yml

server:port: 8080mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: truecache-enabled: truemapper-locations: classpath:mapper/*Mapper.xmlglobal-config:db-config:id-type: assign_uuidlogic-delete-value: 1logic-not-delete-value: 0logic-delete-field: is_delwhere-strategy: not_empty update-strategy: not_emptyinsertStrategy: not_empty
spring:datasource:driver-class-name: com.p6spy.engine.spy.P6SpyDriverusername: rootpassword: rooturl: jdbc:p6spy:mysql://localhost:3306/lrc_blog?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaifreemarker:suffix: .html


MyTokenUtil.java

package work.linruchang.qq.mybaitsplusjoin.common.util;import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.json.JSONUtil;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Optional;/**** 根据<a href="https://jwt.io/introduction"></a> 进行构建标准Token* @author LinRuChang* @version 1.0* @date 2022/08/06* @since 1.8**/
public class MyTokenUtil {private static Dict HEADER = Dict.create();static {//签名算法可以是HS256 或者 RSA 我这里写死成HS256算法HEADER.set("alg", "HS256").set("typ", "JWT");}private static Dict DEFAULT_PAYLOAD = Dict.create();static {DEFAULT_PAYLOAD.set("iss", "发行系统:mybaits-plus-join项目").set("exp", null)  //过期时间  毫秒.set("cre", null) //创建时间 毫秒.set("sub ", "可访问资源:系统任何资源都可访问").set("aud", "授权给谁:超级管理员");}/*** 签名密钥*/private static final String secret = "sdfsdfds";/*** 生成token** @param payload 负载* @return*/public static String createToken(Dict payload) {Dict allPayload = new Dict(DEFAULT_PAYLOAD);if (CollUtil.isNotEmpty(payload)) {allPayload.putAll(payload);}if(ObjectUtil.isEmpty(allPayload.get("cre"))) { //创建时间allPayload.put("cre", new Date().getTime());}String HEADERJson = JSONUtil.toJsonStr(HEADER);String HEADERJson_Base64Url = Base64.encodeUrlSafe(HEADERJson);String allPayloadJson = JSONUtil.toJsonStr(allPayload);String allPayload_Base64Url = Base64.encodeUrlSafe(allPayloadJson);HMac mac = new HMac(HmacAlgorithm.HmacSHA256, secret.getBytes());String signSouceContent = StrUtil.format("{}.{}", HEADERJson_Base64Url, allPayload_Base64Url);String signature = mac.digestBase64(signSouceContent, true);return StrUtil.format("{}.{}.{}", HEADERJson_Base64Url, allPayload_Base64Url, signature);}/*** 校验token*  签名匹配且未过期* @param token* @return*/public static boolean verify(String token) {List<String> splitContent = StrUtil.split(token, ".");boolean verifyFlag = false;if (CollUtil.size(splitContent) == 3) {String HEADERJson_Base64Url = splitContent.get(0);String allPayload_Base64Url = splitContent.get(1);String signature = splitContent.get(2);//系统根据前面头信息以及负载生成签名HMac mac = new HMac(HmacAlgorithm.HmacSHA256, secret.getBytes());String systemSignSourceContent = StrUtil.format("{}.{}", HEADERJson_Base64Url, allPayload_Base64Url);String systemSignature = mac.digestBase64(systemSignSourceContent, true);//比对签名if (StrUtil.equals(systemSignature, signature)) {String allPayloadJson = Base64.decodeStr(allPayload_Base64Url);Dict dict = JSONUtil.toBean(allPayloadJson, Dict.class);String exp = dict.getStr("exp");//比对token有效期if (StrUtil.isBlank(exp) || (exp != null && new Date().getTime() < dict.getLong("exp"))) {verifyFlag = true;}}}return verifyFlag;}/*** 获取负载信息* @param token* @return*/public static Dict parse(String token) {if (verify(token)) {String allPayloadJson = Base64.decodeStr(StrUtil.split(token, ".").get(1));return JSONUtil.toBean(allPayloadJson, Dict.class);}return null;}/*** 根据Bearer认证标准从用户请求中获取token <a href=“https://datatracker.ietf.org/doc/html/rfc6750”></a>* @return*/public static String getCurrentRequestToken()  {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String authorization = request.getHeader("Authorization");String token = null;//请求头if(StrUtil.isNotBlank(authorization)) {List<String> authorizationInfos = StrUtil.splitTrim(authorization, StrUtil.SPACE);if(CollUtil.size(authorizationInfos) == 2 && StrUtil.equals(authorizationInfos.get(0), "Bearer")) {token = authorizationInfos.get(1);}} else if(StrUtil.equalsIgnoreCase(request.getMethod(),"GET") || (StrUtil.equalsIgnoreCase(request.getMethod(),"POST") && StrUtil.containsIgnoreCase(request.getHeader("Content-Type"),"application/x-www-form-urlencoded"))) {token = request.getParameter("access_token");}return Optional.ofNullable(token).orElse(null);}public static void main(String[] args) {String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiLlj5HooYzns7vnu5_vvJpteWJhaXRzLXBsdXMtam9pbumhueebriIsInN1YiAiOiLlj6_orr_pl67otYTmupDvvJrns7vnu5_ku7vkvZXotYTmupDpg73lj6_orr_pl64iLCJhdWQiOiLmjojmnYPnu5nosIHvvJrotoXnuqfnrqHnkIblkZgiLCJ1c2VyTmFtZSI6ImxyYyJ9._Au456JLDQ4yIlkBYo8xiHTklrn1b2AMp46KHuKrfIU";boolean verify = verify(token);Dict parseInfo = parse(token);Console.log(verify);Console.log(parseInfo);}}


BearerAuthInterceptor.java


/*** 作用:Bearer认证-其实就是token认证** @author LinRuChang* @version 1.0* @date 2022/08/04* @since 1.8**/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class BearerAuthInterceptor implements HandlerInterceptor {/*** 认证错误信息*/@AllArgsConstructor@Getterenum AuthErrorEnum {INVALID_REQUEST(400, "invalid_request", "请求不符合Bearer认证标准"),INVALID_TOKEN(401, "invalid_token", "认证失败"),INSUFFICIENT_SCOPE(402, "insufficient_scope", "token权限不足");Integer errorCode;String error;String errorDescription;}/*** key:token* List<String>:token可访问的链接*/public final static ConcurrentHashMap<String, List<String>> allSystemTokensMap = new ConcurrentHashMap();@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {boolean authStatusFlag = false;AuthErrorEnum authErrorEnum = AuthErrorEnum.INVALID_TOKEN;String token = MyTokenUtil.getCurrentRequestToken(); //当前的请求头tokenString uri = request.getRequestURI(); //当前的uri链接if (StrUtil.isNotBlank(token)) {boolean verifyToken = MyTokenUtil.verify(token);if (CollUtil.contains(allSystemTokensMap.keySet(), token) && verifyToken) {List<String> pathPatternList = allSystemTokensMap.get(token);if (CollUtil.isNotEmpty(pathPatternList)) {AntPathMatcher antPathMatcher = new AntPathMatcher();authStatusFlag = pathPatternList.stream().anyMatch(pathPattern -> antPathMatcher.match(pathPattern, uri));if (!authStatusFlag) { //此token权限不足以访问此请求内容authErrorEnum = AuthErrorEnum.INSUFFICIENT_SCOPE;}} else {authStatusFlag = true;}} else { //token非法或过期,从系统allSystemTokens中排除allSystemTokensMap.remove(token);}} else { //token缺失-没传或者没按规范进行装填TokenauthErrorEnum = AuthErrorEnum.INVALID_REQUEST;}//认证失败if (!authStatusFlag) {response.setStatus(authErrorEnum.getErrorCode());response.setHeader("WWW-Authenticate", StrUtil.format("Bearer realm=\"admin token\";charset=UTF-8;error=\"{}\";error_description=\"{}\"", authErrorEnum.getError(), URLUtil.encode(authErrorEnum.getErrorDescription())));response.setHeader("Content-Type", "application/json;charset=UTF-8");response.getWriter().write(JSONUtil.toJsonStr(Dict.create().set("msg", authErrorEnum.getErrorDescription()).set("code", authErrorEnum.getErrorCode())));}return authStatusFlag;}
}


MyConfig.java

@Configuration
public class MyConfig implements WebMvcConfigurer {@AutowiredBearerAuthInterceptor bearerAuthInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(bearerAuthInterceptor).addPathPatterns("/**").excludePathPatterns("/js/**","/user*/**","/user*/**");}
}


ArticleCategoryController.java

@RestController
@RequestMapping("article-category")
public class ArticleCategoryController {@AutowiredArticleCategoryService articleCategoryService;/*** 根据ID进行查询* @param id* @return*/@GetMapping("/one/{id}")public CommonHttpResult<ArticleCategory> findById(@PathVariable("id") String id) {return CommonHttpResult.success(articleCategoryService.getById(id));}}


ArticleCommentController.java

@RestController
@RequestMapping("article-comment")
public class ArticleCommentController {@AutowiredArticleCommentService articleCommentService;/*** 根据ID进行查询* @param id* @return*/@GetMapping("/one/{id}")public CommonHttpResult<ArticleComment> findById(@PathVariable("id") String id) {return CommonHttpResult.success(articleCommentService.getById(id));}}


UserController3.java

@Controller
@RequestMapping("user3")
public class UserController3 {/*** 登录页面** @param httpServletRequest* @param modelAndView* @return*/@GetMapping("login")public ModelAndView loginPage(HttpServletRequest httpServletRequest, ModelAndView modelAndView) {modelAndView.setViewName("login3");return modelAndView;}/*** 登录** @param httpServletRequest* @param httpServletResponse* @param modelAndView*/@PostMapping("login")@SneakyThrows@ResponseBodypublic Dict login(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, ModelAndView modelAndView) {String userName = httpServletRequest.getParameter("userName");String password = httpServletRequest.getParameter("password");if (StrUtil.equals(userName, "admin") && StrUtil.equals(password, "admin123")) {Dict tokenPayload = Dict.create().set("num", UUID.fastUUID().toString(true)).set("userName", userName).set("credentials", SecureUtil.md5(StrUtil.format("{}:{}", userName, password)));String accessToken = MyTokenUtil.createToken(tokenPayload);BearerAuthInterceptor.allSystemTokensMap.put(accessToken, Arrays.asList("/article-category/**"));return Dict.create().set("code","success").set("msg","登录成功==每次请求其他接口时请将access_token放置到请求头上传给服务器").set("access_token",accessToken).set("expires_in", -1).set("token_type","Bearer");}return Dict.create().set("code","failure").set("msg","登录失败");}/*** 退出登录 == 命令浏览器删除cookie** @return*/@GetMapping("logout")@ResponseBodypublic Dict logout() {String currentRequestToken = MyTokenUtil.getCurrentRequestToken();BearerAuthInterceptor.allSystemTokensMap.remove(currentRequestToken);return Dict.create().set("code", "success").set("msg", StrUtil.isNotBlank(currentRequestToken) ? StrUtil.format("token已失效:{}", currentRequestToken) : null);}}


login3.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="/js/jquery.min.js"></script><style>.testRequest {display: block;margin-bottom: 15px;}</style>
</head>
<body>
<table border="1"><form action="/user3/login" method="post"><tr><td>账号:</td><td><input id="userName" name="userName" type="text"></td></tr><tr><td>密码:</td><td><input id="password" name="password"  type="text"></td></tr><tr><td colspan="2" style="text-align: center"><!--<button id="loginBtn" type="submit">登录</button>--><button id="loginBtn" type="button">登录</button><!--<button id="logoutBtn" type="button" ><a href="/user3/logout?access_token=">退出</a></button>--><button id="logoutBtn" type="button" >退出</button></td></tr></form></table>
<div><span style="font-weight: bolder">当前用户Token:</span><span id="currentUser"></span></div><div style="margin-top: 20px;">请填入access_token:<input id="accessToken" type="text" value="" placeholder="请填入access_token">
</div>
<a target="_blank" id="testRequest1" tar class="testRequest" href="/article-category/one/c08d391e02bc11eb9416b42e99ea3e62?access_token=">测试请求(token有权限):<span id="testRequest1Href">/article-category/one/c08d391e02bc11eb9416b42e99ea3e62?access_token2=</span></a>
<a target="_blank" id="testRequest2" class="testRequest" href="/article-comment/one/1346e1060e95de36d8d8a7bbc8925dfb?access_token=">测试请求(token无访问权限):<span id="testRequest2Href">/article-category/one/c08d391e02bc11eb9416b42e99ea3e62?access_token2=</span></a>
</body>
<script>$(function () {var logoutLink = '/user3/logout?access_token=';$("input[id='accessToken']").bind('input propertychange', function() {var access_token = $(this).val();logoutLink = logoutLink.substring(0,logoutLink.indexOf('=')) + '=' + access_token;$(".testRequest").each(function (index) {href = $(this).attr("href")console.log(href)var hrefPrefix = href.substring(0,href.indexOf('='))var newHref = hrefPrefix + '=' + access_token;$(this).attr("href", newHref);$('#testRequest' + (index+1) + 'Href').text(newHref)})});$("#currentUser").text(localStorage.getItem("access_token"))/*** 登录*/$("#loginBtn").click(function () {var userName = $("#userName").val();var password = $("#password").val();$.post("/user3/login",{userName: userName,password : password}, function (data) {console.log(data)if(data.access_token) {localStorage.setItem("access_token",data.access_token);$("#currentUser").text(localStorage.getItem("access_token"))}alert(data.msg + ": " + (data.access_token?data.access_token:''));},'json')})/*** 退出*/$("#logoutBtn").click(function () {var inputToken = $("input[id='accessToken']").val()if(!$("input[id='accessToken']").val()) {alert("请输入需要退出的Token")return;}if(localStorage.getItem("access_token") == inputToken) {localStorage.removeItem("access_token")$("#currentUser").text('')}console.log('退出:' + logoutLink)$.get(logoutLink,{}, function (data) {alert(data.msg)},'json')})})</script></html>
演示(Bearer认证)
浏览器

postman

认证学习4 - Bearer认证(Token认证)讲解、代码实现、演示相关推荐

  1. php 公众号token认证,微信公众号开发——Token认证

    公众号开发第一步就是绑定Token,Token认证相当于把我们的公众号和服务器关联起来,只有Token认证成功了我们的服务器才能接收到来自公众号的消息.微信官方回调的地址必须能在公网上访问,后端服务的 ...

  2. 常用的认证机制之session认证和token认证

    一.session认证 1.session认证的过程: 前端输入用户名和密码进行登录操作,后端拿到用户名和密码后,会把md5进行加密,加密之后,拿上加密后的密文到用户表中查找密文是否一致,判断用户是否 ...

  3. jwt重放攻击_【干货分享】基于JWT的Token认证机制及安全问题

    一步一步教你基于JWT的Token认证机制实现,以及如何防范XSS攻击.Replay攻击和中间人攻击. 文章目录 一.几种常用的认证机制 1.1 HTTP Basic Auth HTTP Basic ...

  4. 登陆验证发展史(cookie认证->session认证->token认证->JWT,单系统登陆->多系统单点登陆)

    登陆验证发展史有两条主线.在服务部署方式层面,早期的Web服务系统简单一般都是单系统,登陆的话就登陆这一个系统就好了,随着系统复杂性越来越高,一个大的系统往往由很多子系统组成,用户使用这个大系统时不可 ...

  5. 网关信息认证服务器不可达,网关消息认证服务器不可达

    网关消息认证服务器不可达 内容精选 换一换 API的响应码如何定义?响应信息由后端API服务定义,API网关只做透传.响应信息由后端API服务定义,API网关只做透传.使用VPC通道,后端服务的主机端 ...

  6. jwt接口认证方式:Bearer Token

    jwt接口认证方式:Bearer Token 问题背景 在以前,用户进行认证的时候一般是: -> 用户向服务端发送验证信息(用户名.密码). -> 服务端验证成功就向用户返回一个sessi ...

  7. Spring Cloud与微服务学习总结(6)——认证鉴权与API权限控制在微服务架构中的设计与实现(四)

    本文转载自(http://blueskykong.com/2017/10/26/security4/) 1. 前文回顾 首先还是照例对前文进行回顾.在第一篇 认证鉴权与API权限控制在微服务架构中的设 ...

  8. Spring Cloud与微服务学习总结(3)——认证鉴权与API权限控制在微服务架构中的设计与实现(一)

    本文转载自(http://blueskykong.com/2017/10/19/security1/) 1. 背景 最近在做权限相关服务的开发,在系统微服务化后,原有的单体应用是基于session的安 ...

  9. Node.js 学习之数据库与身份认证

    数据库与身份认证 文章目录 数据库与身份认证 1.SQL 的相关学习 1. SQL 的概念 2. SQL 语句学习 a. 查询数据(select).插入数据(insert into).更新数据(upd ...

最新文章

  1. 如何编写一个npm包,可以公共使用?
  2. mysql二进制文件复制_MySQL 主从复制:基于二进制文件复制配置详解
  3. 安卓版kindle电子书位置_安卓手机APP如何打开三种格式的电子书文件
  4. Spring Cloud Config git版
  5. HDU 1875 畅通工程再续
  6. 前端入行两年--教会了我这些道理
  7. JAVAWEB企业级开发关于Tomcat的安装和配置
  8. JAVA逆向反混淆-追查Burpsuite的破解原理
  9. hadoop交流群261039241
  10. ajax谷歌浏览器没缓存,从缓存AJAX请求会导致Chrome(Prevent Chrome from caching AJA
  11. 麦腾股份正式挂牌新三板,创业孵化市场或将深度洗牌
  12. 垃圾邮件过滤python_手把手教你用Python 和 Scikit-learn 实现垃圾邮件过滤
  13. 2019牛客暑期多校训练营(第八场) Beauty Values
  14. 硬盘结构及raid磁盘阵列
  15. excel入门,如何玩转excel,你早该这么玩Excel笔记12
  16. 华为、联想:外媒眼中的“中国制造”
  17. 亚马逊的规则你知道多少
  18. linux查看wifi网速,无线信号强度解析及linux如何查看wifi信号强弱等
  19. 3D折纸效果怎么实现?
  20. CSS实现背景网格线(background-image)

热门文章

  1. hdu 1789题解
  2. 小程序swiper切换闪屏问题
  3. input边框怎么去掉
  4. strtok 详解及实现
  5. 搭建wnmp开发环境
  6. python的or的用法_python中or和and的用法
  7. input type=“radio“默认按钮选中状态
  8. html radio 默认选中
  9. CLR的主要作用有哪些
  10. Qt | QTableView的使用方法