登录拦截

登录拦截

因为项目中涉及到这个,自己也接触了,所以记录一下。
认证过程
登录:
1.第一次认证:第一次登录,用户从浏览器输入用户名/密码,提交到服务器的登录处理的Controller层。

QueryWrapper<Person> queryWrapper = new QueryWrapper();
        queryWrapper.eq("data_status", GlobalConstant.DATA_STATUS_VALID);       queryWrapper.eq("phone",param.get("username")).eq("password",MD5Util.getMD5(param.get("password").toString())).or(i ->i.eq("email",param.get("username")).eq("password",MD5Util.getMD5(param.get("password").toString())).eq("data_status", GlobalConstant.DATA_STATUS_VALID));
        Person one = personService.getOne(queryWrapper);

2.Controller调用认证服务进行用户名密码认证,如果认证通过,Controller层调用用户信息服务获取用户信息;

System.out.println("登录成功!");
    String id = one.getUuid();//id 当前用户ID

3.返回用户信息后,Controller从配置文件中获取Token签名生成的秘钥信息,进行Token的生成;

//issuer 该JWT的签发者,是否使用是可选的
      	String issuer = "";
      	//subject 该JWT所面向的用户,是否使用是可选的
      	String subject = "";
      	//ttlMillis 什么时候过期,这里是一个Unix时间戳,是否使用是可选的
      	long ttlMillis = 1000 * 60 * 30; 
      	//audience 接收该JWT的一方,是否使用是可选的
      	String audience = "";
      	String token = TokenUtil.createJWT(id,issuer,subject,ttlMillis,audience);
		public static String createJWT(String id,String issuer,String subject,long ttlMillis, String audience){
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(APP_KEY);
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
        JwtBuilder jwtBuilder = Jwts.builder()
                .setId(id)
                .setSubject(subject)
                .setIssuedAt(now)
                .setIssuer(issuer)
                .setAudience(audience)
                .signWith(signatureAlgorithm,signingKey);
	//设置Token的过期时间
        if(ttlMillis >0){  
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            jwtBuilder.setExpiration(exp);
        }
        return jwtBuilder.compact();
    }

4.生成Token的过程中可以调用第三方的JWT Lib生成签名后的JWT数据;

String token = TokenUtil.createJWT(id,issuer,subject,ttlMillis,audience);

5.完成JWT数据签名后,将其设置到COOKIE对象中,并重定向到首页,完成登录过程;

HashMap<String,String> result = new HashMap<>();
 	result.put("userId", id);
 	result.put("token", token);	
 	return ResultFactory.success(result);

请求认证:
基于Token的认证机制会在每一次请求中都带上完成签名的Token信息,这个Token信息可能在COOKIE中,也可能在HTTP的Authorization头中;

  • 客户端(APP客户端或浏览器)通过GET或POST请求访问资源(页面或调用API);
  • 认证服务作为一个Middleware HOOK 对请求进行拦截,首先在cookie中查找Token信息,如果没有找到,则在HTTP Authorization Head中查找;
  • 如果找到Token信息,则根据配置文件中的签名加密秘钥,调用JWT Lib对Token信息进行解密和解码;
  • 完成解码并验证签名通过后,对Token中的exp、nbf、aud等信息进行验证;
  • 全部通过后,根据获取的用户的角色权限信息,进行对请求的资源的权限逻辑判断;
  • 如果权限逻辑判断通过则通过Response对象返回;否则则返回HTTP 401;

相关类:
登录Controller类

@RequestMapping("/login")
	public Result login(@RequestBody Map<String, Object> param) {
	QueryWrapper<Person> queryWrapper = new QueryWrapper();
    queryWrapper.eq("data_status", GlobalConstant.DATA_STATUS_VALID);
	queryWrapper.eq("phone",param.get("username")).eq("password",MD5Util.getMD5(param.get("password").toString())).or(i ->i.eq("email",param.get("username")).eq("password",MD5Util.getMD5(param.get("password").toString())).eq("data_status", GlobalConstant.DATA_STATUS_VALID));
        Person one = personService.getOne(queryWrapper);
        if(one !=null) {
        	System.out.println("登录成功!");
        	//id 当前用户ID
        	String id = one.getUuid();
        	//issuer 该JWT的签发者,是否使用是可选的
        	String issuer = "";
        	//subject 该JWT所面向的用户,是否使用是可选的
        	String subject = "";
        	//ttlMillis 什么时候过期,这里是一个Unix时间戳,是否使用是可选的
        	long ttlMillis = 1000 * 60 * 30; 
        	//audience 接收该JWT的一方,是否使用是可选的
        	String audience = "";
        	String token = TokenUtil.createJWT(id,issuer,subject,ttlMillis,audience);
        	HashMap<String,String> result = new HashMap<>();
        	result.put("userId", id);
        	result.put("token", token);  	
        	return ResultFactory.success(result);
        }
        System.out.println("登录失败!");
        return ResultFactory.fail(null);
	}

TokenInterceptor拦截器:

@Component
public class TokenInterceptor implements HandlerInterceptor{
	 @Override
	    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
	            throws Exception {
		 //获取token
	        String tokenEnmu= request.getHeader("authorization");     
	        if(!CommonUtils.isEmpty(tokenEnmu)) {
	        	try {
	        		Claims claims = TokenUtil.getClaims(tokenEnmu);
		        	String personId = claims.getId();
		        	Date dateExpiration = claims.getExpiration();
		        	if(!CommonUtils.isObjEmpty(dateExpiration)) {
		        		Person person = new Person();
		        		person.setUuid(personId);
		        		//threadlocal保存用户id,用来更新操作记录
		        		ThreadLocalUtil.setResources(person);
		        		//更新时间戳,用来生成token
		        		long ttlMillis = 1000 * 60 * 30;
		        		String newtoken = TokenUtil.createJWT(personId,claims.getIssuer(),claims.getSubject(),ttlMillis,claims.getAudience());
		        		//将token设置到浏览器
		        		response.setHeader("authorization", newtoken);
		        	}else {
		        		response.setStatus(401);
		        	}
				} catch (Exception e) {
					response.setStatus(401);
				}	
	        }else {
	        	response.setStatus(401);
	        }
	        //返回 false 则请求中断
	        return true;
	    }
	    @Override
	    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
	            ModelAndView modelAndView) throws Exception {
//	        log.info("postHandle:请求后调用");
//	    	获取塞入的用户id
//	    	Person personInfo =  ThreadLocalUtil.getResources();
//	    	System.out.println("用户id:"+personInfo.getUuid()+"-----------");
	    }
	    @Override
	    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
	            throws Exception {
//	        log.info("afterCompletion:请求调用完成后回调方法,即在视图渲染完成后回调");
	    }    
}

自定义拦截器:

@Configuration
public class TokenConfig extends WebMvcConfigurationSupport{	
	@Autowired
	private TokenInterceptor tokenInterceptor;
	@Override
	protected void addInterceptors(InterceptorRegistry registry) {
	registry.addInterceptor(tokenInterceptor).addPathPatterns("/**").excludePathPattern		s("/api/login", "/api/file/upload","/api/file/ioimage", "/api/kafka/send/*", 			    "/api/basedata", "/api/report/**");
	//	super.addInterceptors(registry);
	}
}

TokenUtil工具类:

public class TokenUtil {
    private static final String APP_KEY = "user_key"; //进行数字签名的私钥
    /**
     * 一个JWT实际上就是一个字符串,它由三部分组成,头部(Header)、载荷(Payload)与签名(Signature)
     * @param id 当前用户ID
     * @param issuer 该JWT的签发者,是否使用是可选的
     * @param subject 该JWT所面向的用户,是否使用是可选的
     * @param ttlMillis 什么时候过期,这里是一个Unix时间戳,是否使用是可选的
     * @param audience 接收该JWT的一方,是否使用是可选的
     * @return
     */
    public static String createJWT(String id,String issuer,String subject,long ttlMillis, String audience){
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(APP_KEY);
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
        JwtBuilder jwtBuilder = Jwts.builder()
                .setId(id)
                .setSubject(subject)
                .setIssuedAt(now)
                .setIssuer(issuer)
                .setAudience(audience)
                .signWith(signatureAlgorithm,signingKey);
	//设置Token的过期时间
        if(ttlMillis >0){  
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            jwtBuilder.setExpiration(exp);
        }
        return jwtBuilder.compact();
    } 
    //私钥解密token信息
    public static Claims getClaims(String jwt) {
        return Jwts.parser()
                .setSigningKey(DatatypeConverter.parseBase64Binary(APP_KEY))
                .parseClaimsJws(jwt).getBody();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值