何为token?【如果想直接看代码可以往下翻】
使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:
1. 客户端使用用户名跟密码请求登录
2. 服务端收到请求,去验证用户名与密码
3. 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
4. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里
5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
6. 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
token优点
支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通
过HTTP头传输.
无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登
录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.
更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服
务端只要提供API即可.
去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你
可以进行Token生成调用即可.
更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的
(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。
CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。
性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析
要费时得多.
不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理.
基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby,
Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)
如何创建token【我这里用的是Jwt机制,有些公司用的是自己的生成方法哈】
(1)创建maven工程,引入依赖
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.6.0</version> </dependency>
(2)使用工具类JwtUtils
1 /** 2 * @author: Mr.Yang 3 * @create: 2020-02-13 21:19 4 **/ 5 @Getter 6 @Setter 7 @ConfigurationProperties("jwt.config") 8 public class JwtUtils { 9 //签名私钥 10 private String key; 11 //签名失效时间 12 private Long failureTime; 13 14 /** 15 * 设置认证token 16 * 17 * @param id 用户登录ID 18 * @param subject 用户登录名 19 * @param map 其他私有数据 20 * @return 21 */ 22 public String createJwt(String id, String subject, Map<String, Object> map) { 23 24 //1、设置失效时间啊 25 long now = System.currentTimeMillis(); //毫秒 26 long exp = now + failureTime; 27 28 //2、创建JwtBuilder 29 JwtBuilder jwtBuilder = Jwts.builder().setId(id).setSubject(subject) 30 .setIssuedAt(new Date()) 31 //设置签名防止篡改 32 .signWith(SignatureAlgorithm.HS256, key); 33 34 //3、根据map设置claims 35 for (Map.Entry<String, Object> entry : map.entrySet()) { 36 jwtBuilder.claim(entry.getKey(), entry.getValue()); 37 } 38 jwtBuilder.setExpiration(new Date(exp)); 39 40 //4、创建token 41 String token = jwtBuilder.compact(); 42 return token; 43 } 44 45 /** 46 * 解析token 47 * 48 * @param token 49 * @return 50 */ 51 public Claims parseJwt(String token) { 52 Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody(); 53 return claims; 54 } 55 56 }
注意:
@ConfigurationProperties("jwt.config")需要在配置文件中配置
(3)配置JwtUtils类
1 /** 2 * 配置jwt 3 * 4 * @return 5 */ 6 @Bean 7 public JwtUtils jwtUtils() { 8 return new JwtUtils(); 9 }
(4)编写登录方法
1、编写DAO层
1 /** 2 * @author: Mr.Yang 3 * @create: 2020-02-13 21:55 4 **/ 5 @Repository 6 public interface UserDAO { 7 8 public User selectByMobileUser(String mobile); 9 10 public User selectByIdUser(String id); 11 }
2、编写xml写Sql
<select id="selectByMobileUser" parameterType="string" resultMap="userMap"> select * from bs_user where mobile = #{mobile} </select>
3、编写service层
/** * 根据mobile查询用户 * * @param mobile * @return */ public User selectByMobile(String mobile) { return userDAO.selectByMobileUser(mobile); }
4、编写controller层
1 /** 2 * 用户登录 3 * 1.通过service根据mobile查询用户 4 * 2.比较password 5 * 3.生成jwt信息 6 * 7 * @param loginMap 8 * @return 9 * @requestBody把请求数据封装(前端以json方式传) 10 */ 11 @RequestMapping(value = "/login", method = RequestMethod.POST) 12 public Result login(@RequestBody Map<String, String> loginMap) { 13 String mobile = loginMap.get("mobile"); 14 String password = loginMap.get("password"); 15 User user = userService.selectByMobile(mobile); 16 //登录失败 17 if (user == null || !user.getPassword().equals(password)) { 18 //既可以使用抛异常,也可使用直接返回错误码(推荐) 19 return new Result(ResultCode.MOBILEORPASSWORDERROR); 20 } else { 21 //其他数据以map集合存放在token中 22 Map<String, Object> dataMap = new HashMap<>(); 23 dataMap.put("companyId", user.getCompanyId()); 24 dataMap.put("companyName", user.getCompanyName()); 25 //生成token并存入数据返回 26 String token = jwtUtils.createJwt(user.getId(), user.getUsername(), dataMap); 27 return new Result(Result.SUCCESS(), token); 28 } 29 }
5、测试
data就是返回的token,里面存有用户ID,用户姓名及以map形式存入的数据(这里主要看前端需要什么就存什么)
(6)用户登录成功之后,获取用户信息
1 /** 2 * 用户登录成功之后,获取用户信息 3 * 1.获取用户id 4 * 2.根据用户id查询用户 5 * 3.构建返回值对象 6 * 4.响应 7 * 8 * @param request 9 * @return 10 * @throws Exception 11 */ 12 @RequestMapping(value = "/profile", method = RequestMethod.POST) 13 public Result profile(HttpServletRequest request) throws PendingException, Exception { 14 15 /** 16 * 从请求头信息中获取token数据 17 * 1.获取请求头信息:名称=Authorization(前后端约定) 18 * 2.替换Bearer+空格 19 * 3.解析token 20 * 4.获取clamis 21 */ 22 23 24 //1.获取请求头信息:名称=Authorization(前后端约定) 25 String authorization = request.getHeader("Authorization"); 26 if (StringUtils.isEmpty(authorization)) { 27 // throw new PendingException(ResCode.UNAUTHENTICATED); 28 //系统未捕捉到请求头信息 29 throw new CommonException(ResultCode.UNAUTHENTICATED); 30 } 31 //2.替换Bearer+空格 32 String token = authorization.replace("Bearer ", ""); 33 34 //3.解析token 35 Claims claims = jwtUtils.parseJwt(token); 36 //4.获取clamis 37 String userId = claims.getId(); 38 39 // String userId = "U01"; 40 User user = userService.selectByIdUser(userId); 41 42 /**此处只是为了获取token中的用户数据,所有只简单返回用户对象, 43 * 工作则按实际要求多表查询需要数据(根据用户ID查询权限) 44 */ 45 46 return new Result(ResultCode.SUCCESS, user); 47 }
(7)测试