springboot+jwt+aop+异常统⼀处理+token验证实现登陆功能
java实现基于JWT的token登陆认证
前⾔
之前基于session的登录⽅式,是在⽤户登录成功后将⽤户信息存⼊到session中,这样不利于程序的横向扩展, 如果将项⽬部署多份,会出现session漂移的问题,并且随着登录⽤户的增加,会不断的占⽤服务端的内存资源;⽽现在这种基于token的登录⽅式,是在登录成功后 将⽤户信息存⼊到客户端中,不会额外占⽤服务端的内存资源,并且通过签名和验签可以保证数据不被篡改,
⼜因为登录成功后是将⽤户的信息存⼊到客户端中,所以在进⾏横向扩展,部署多份的时候,不会产⽣session漂移的问题。
在项⽬中的使⽤
导⼊所需依赖
<!--引⼊JWT依赖,由于是基于Java,所以需要的是java-jwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
项⽬后台代码
登录认证相关代码
写⼀个简单的登陆测试⼀下怎么使⽤token
bean
public class User {
private Long userId;
private String userName;
private String password;
}
核⼼⽅法
package com.fh.jwt;
import com.sponse.ResponseServer;
import com.sponse.ServerEnum;
import io.jsonwebtoken.*;
import sun.misc.BASE64Encoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtTokenUtils {
public static String createToken(Map<String,Object> map){
//jwt如何⽣成字符串
//声明头部信息
Map<String,Object> headerMap=new HashMap<String,Object>();
headerMap.put("alg","HS256");
headerMap.put("typ","JWT");
//设置负载:不要放着涉密的东西⽐如:银⾏账号密码,余额,⾝份证号 Map<String,Object> payload=new HashMap<String,Object>();
payload.putAll(map);
Long iat=System.currentTimeMillis();
//设置jwt的失效时间⼀分钟
Long endTime = iat+60000l;
//签名值就是我们的安全密钥
String token=Jwts.builder()
.setHeader(headerMap)
.setClaims(payload)
.setExpiration(new Date(endTime))
.signWith(SignatureAlgorithm.HS256,getSecretKey("secretKey"))
pact();
return token;
}
public static ResponseServer resolverToken(String token ){
Claims claims=null;
try{
claims = Jwts.parser()
.setSigningKey(getSecretKey("secretKey"))
.parseClaimsJws(token)
.getBody();
}catch(ExpiredJwtException exp){
System.out.println("token超时,token失效了");
(ServerEnum.TOKEN_TIMEOUT);
}catch(SignatureException sing){
System.out.println("token解析失败");
(ServerEnum.SAFETY_ERROR);
}
return ResponseServer.success(claims);
}
private static String getSecretKey(String key){
return new BASE64Encoder().Bytes());
}
}
登录认证相关代码
控制层
package ller;
import com.fh.jwt.JwtTokenUtils;
import del.User;
import com.fh.service.UserService;
import kenauth.TokenCheckAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@CrossOrigin
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
public UserService service;
@RequestMapping("login")
public Map<String,Object>login(String userName,String password){
HashMap<String, Object> map =new HashMap<>();
User u = service.queryUser(userName);
if(u == null){
map.put("code",3000);
return map;
}
// ⽣成token
Map<String,Object> m =new HashMap<>();
m.put("id",u.getUserId());
String token = ateToken(m);
map.put("token",token);
return map;
}
//@TokenCheckAnnotation 是⾃定义的注解,在需要进⾏登录认证的所有⽅法加上注解//客户端访问本⽅法时会拦截
@TokenCheckAnnotation
@RequestMapping("test")
public void test(){
System.out.println("我被调⽤了");
}
}
⾃定义注解
package kenauth;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;springboot aop
import java.lang.annotation.Target;
@Target(ElementType.METHOD)// 修饰范围
@Retention(RetentionPolicy.RUNTIME)// ⽤来描述注解的声明周期
public @interface TokenCheckAnnotation {
}
登录验证
附⼀个随便的状态码
就⽤到了⼏个,其他的都⽤不到
package com.sponse;
public enum ServerEnum {
SUCCESS(200,"操作成功"),
DEL_DEPT_SCUCCESS(201,"删除部门成功"),
LOGIN_ISNULL(5000,"⽤户名或者密码为空"),
PHONE_ISNULL(5007,"⼿机号不能为空"),
USERNAME_NOTEXIST(5001,"⽤户名输⼊有误。"),
PASSWORD_WRONG(5002,"密码输⼊错误,请检查"),
LOGIN_SUCCESS(5003,"登陆成功"),
LOGIN_EXPIRED(5004,"登录超时,请重新登陆"),
SECRET_ERROR(5005,"传⼊的token值有误,不能通过签名验证"), TOKEN_TIMEOUT(5006,"登录失效,请重新登录"),
TOKEN_ISNULL(5008,"获取到的Token值为空"),
NO_MENU_RIGHT(6000,"没有权限访问该菜单,请联系管理员"), NOT_DATA(7001,"没有要导出的数据"),
HTTP_URL_ISNULL(8002,"你传递的URL路径为空了"),
SERVER_TIMEOUT(8004,"服务连接请求超时"),
HTTP_ERROR(8003,"接⼝访问失败"),
SERVER_STOP(8005,"服务连接不上"),
SAFETY_ERROR(9000,"接⼝验签失败"),
SAFETY_BAD(9001,"接⼝被⾮法攻击"),
SAFETY_TIMEOUT(9002,"接⼝访问超时"),
SAFETY_INVALID(9003,"签名值⽆效"),
SAFETY_REPLAY_ATTACK(9004,"接⼝被重放攻击"),
LOGIN_PHONEORCODE_INNULL(10000,"⼿机号或者验证码为空了"), LOGIN_CODE_ERROR(10001,"⼿机验证码输⼊有误"),
ALL_STOCK_NULL(20001,"商品的库存都不⾜了"),
NO_ORDER_TO_PAY(20002,"没有要⽀付的订单"),
CRATER_PAY_ERROR(20003,"⽣成⽀付⼆维码失败"),
PAY_TIMEOUT(20004,"⽀付超时请刷新页⾯"),
ERROR(500,"操作失败");
private ServerEnum(int code ,String msg){
this.msg=msg;
}
private Integer code;
private String msg;
public Integer getCode(){
return code;
}
public String getMsg(){
return msg;
}
}
对前端返回过来的数据进⾏判断
package com.sponse;
public class ResponseServer {
private Integer code;
private String msg;
private Object data;
private ResponseServer(){
}
private ResponseServer(Integer code,String msg){
this.msg=msg;
}
private ResponseServer(Integer code,String msg,Object data){
this.msg=msg;
this.data=data;
}
/**
* 返回默认的成功状态 200
* @return
*/
public static ResponseServer success(){
return new ResponseServer(Code(),Msg());
}
/**
* 返回默认的带数据成功状态 200
* @param data
* @return
*/
public static ResponseServer success(Object data){
return new ResponseServer(Code(),Msg(),data); }
/**
* 其他特殊类型的成功状态,
* @param serverEnum
* @return
*/
public static ResponseServer success(ServerEnum serverEnum){
return new Code(),Msg());
}
/**
* 带返回数据的其他特殊类型的成功状态
* @param serverEnum
* @param data
* @return
*/
public static ResponseServer success(ServerEnum serverEnum,Object data){
return new Code(),Msg(),data);
}
//失败
public static ResponseServer error(Integer code,String msg){
return new ResponseServer(code,msg);
}
/**
+ * @return
*/
public static ResponseServer error(){
return new ResponseServer(Code(),Msg());
}
/**
* 返回默认的带数据失败状态 500
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论