Spring Boot实战之Filter实现使用JWT进行接口认证 - Go语言中文社区

Spring Boot实战之Filter实现使用JWT进行接口认证


Spring Boot实战之Filter实现使用JWT进行接口认证


jwt(json web token)

用户发送按照约定,向服务端发送 Header、Payload 和 Signature,并包含认证信息(密码),验证通过后服务端返回一个token,之后用户使用该token作为登录凭证,适合于移动端和api


jwt使用流程



本文示例接上面几篇文章中的代码进行编写,请阅读本文的同时可以参考前面几篇文章


1、添加依赖库jjwt,本文中构造jwt及解析jwt都使用了jjwt库

               

<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.6.0</version>
</dependency>



2、添加登录获取token时,所需要的认证信息类LoginPara.java

package com.xiaofangtech.sunt.jwt;

public class LoginPara {
	private String clientId;
	private String userName;
	private String password;
	private String captchaCode;
	private String captchaValue;
	
	public String getClientId() {
		return clientId;
	}
	public void setClientId(String clientId) {
		this.clientId = clientId;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getCaptchaCode() {
		return captchaCode;
	}
	public void setCaptchaCode(String captchaCode) {
		this.captchaCode = captchaCode;
	}
	public String getCaptchaValue() {
		return captchaValue;
	}
	public void setCaptchaValue(String captchaValue) {
		this.captchaValue = captchaValue;
	}
}


3、添加构造jwt及解析jwt的帮助类JwtHelper.java

package com.xiaofangtech.sunt.jwt;

import java.security.Key;
import java.util.Date;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JwtHelper {
	public static Claims parseJWT(String jsonWebToken, String base64Security){
		try
		{
			Claims claims = Jwts.parser()
					   .setSigningKey(DatatypeConverter.parseBase64Binary(base64Security))
					   .parseClaimsJws(jsonWebToken).getBody();
			return claims;
		}
		catch(Exception ex)
		{
			return null;
		}
	}
	
	public static String createJWT(String name, String userId, String role, 
			String audience, String issuer, long TTLMillis, String base64Security) 
	{
		SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
		 
		long nowMillis = System.currentTimeMillis();
		Date now = new Date(nowMillis);
		 
		//生成签名密钥
		byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(base64Security);
		Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
		 
		  //添加构成JWT的参数
		JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
										.claim("role", role)
				                        .claim("unique_name", name)
				                        .claim("userid", userId)
				                        .setIssuer(issuer)
				                        .setAudience(audience)
		                                .signWith(signatureAlgorithm, signingKey);
		 //添加Token过期时间
		if (TTLMillis >= 0) {
		    long expMillis = nowMillis + TTLMillis;
		    Date exp = new Date(expMillis);
		    builder.setExpiration(exp).setNotBefore(now);
		}
		 
		 //生成JWT
		return builder.compact();
	} 
}


4、添加token返回结果类AccessToken.java

package com.xiaofangtech.sunt.jwt;

public class AccessToken {
	private String access_token;
	private String token_type;
	private long expires_in;
	public String getAccess_token() {
		return access_token;
	}
	public void setAccess_token(String access_token) {
		this.access_token = access_token;
	}
	public String getToken_type() {
		return token_type;
	}
	public void setToken_type(String token_type) {
		this.token_type = token_type;
	}
	public long getExpires_in() {
		return expires_in;
	}
	public void setExpires_in(long expires_in) {
		this.expires_in = expires_in;
	}
}


5、添加获取token的接口,通过传入用户认证信息(用户名、密码)进行认证获取

package com.xiaofangtech.sunt.jwt;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.xiaofangtech.sunt.bean.UserInfo;
import com.xiaofangtech.sunt.repository.UserInfoRepository;
import com.xiaofangtech.sunt.utils.MyUtils;
import com.xiaofangtech.sunt.utils.ResultMsg;
import com.xiaofangtech.sunt.utils.ResultStatusCode;

@RestController
public class JsonWebToken {
	@Autowired
	private UserInfoRepository userRepositoy;
	
	@Autowired
	private Audience audienceEntity;
	
	@RequestMapping("oauth/token")
	public Object getAccessToken(@RequestBody LoginPara loginPara)
	{
		ResultMsg resultMsg;
		try
		{
			if(loginPara.getClientId() == null 
					|| (loginPara.getClientId().compareTo(audienceEntity.getClientId()) != 0))
			{
				resultMsg = new ResultMsg(ResultStatusCode.INVALID_CLIENTID.getErrcode(), 
						ResultStatusCode.INVALID_CLIENTID.getErrmsg(), null);
				return resultMsg;
			}
			
			//验证码校验在后面章节添加
			
			
			//验证用户名密码
			UserInfo user = userRepositoy.findUserInfoByName(loginPara.getUserName());
			if (user == null)
			{
				resultMsg = new ResultMsg(ResultStatusCode.INVALID_PASSWORD.getErrcode(),
						ResultStatusCode.INVALID_PASSWORD.getErrmsg(), null);
				return resultMsg;
			}
			else
			{
				String md5Password = MyUtils.getMD5(loginPara.getPassword()+user.getSalt());
				
				if (md5Password.compareTo(user.getPassword()) != 0)
				{
					resultMsg = new ResultMsg(ResultStatusCode.INVALID_PASSWORD.getErrcode(),
							ResultStatusCode.INVALID_PASSWORD.getErrmsg(), null);
					return resultMsg;
				}
			}
			
			//拼装accessToken
			String accessToken = JwtHelper.createJWT(loginPara.getUserName(), String.valueOf(user.getName()),
					user.getRole(), audienceEntity.getClientId(), audienceEntity.getName(),
					audienceEntity.getExpiresSecond() * 1000, audienceEntity.getBase64Secret());
			
			//返回accessToken
			AccessToken accessTokenEntity = new AccessToken();
			accessTokenEntity.setAccess_token(accessToken);
			accessTokenEntity.setExpires_in(audienceEntity.getExpiresSecond());
			accessTokenEntity.setToken_type("bearer");
			resultMsg = new ResultMsg(ResultStatusCode.OK.getErrcode(), 
					ResultStatusCode.OK.getErrmsg(), accessTokenEntity);
			return resultMsg;
			
		}
		catch(Exception ex)
		{
			resultMsg = new ResultMsg(ResultStatusCode.SYSTEM_ERR.getErrcode(), 
					ResultStatusCode.SYSTEM_ERR.getErrmsg(), null);
			return resultMsg;
		}
	}
}


6、添加使用jwt认证的filter

package com.xiaofangtech.sunt.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaofangtech.sunt.jwt.Audience;
import com.xiaofangtech.sunt.jwt.JwtHelper;
import com.xiaofangtech.sunt.utils.ResultMsg;
import com.xiaofangtech.sunt.utils.ResultStatusCode;

public class HTTPBearerAuthorizeAttribute implements Filter{
	@Autowired
	private Audience audienceEntity;

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// TODO Auto-generated method stub
		SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
	            filterConfig.getServletContext());
		
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		// TODO Auto-generated method stub
		
		ResultMsg resultMsg;
		HttpServletRequest httpRequest = (HttpServletRequest)request;
		String auth = httpRequest.getHeader("Authorization");
		if ((auth != null) && (auth.length() > 7))
		{
			String HeadStr = auth.substring(0, 6).toLowerCase();
			if (HeadStr.compareTo("bearer") == 0)
			{
				
				auth = auth.substring(7, auth.length()); 
	            if (JwtHelper.parseJWT(auth, audienceEntity.getBase64Secret()) != null)
	            {
	            	chain.doFilter(request, response);
	            	return;
	            }
			}
		}
		
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		httpResponse.setCharacterEncoding("UTF-8");  
		httpResponse.setContentType("application/json; charset=utf-8"); 
		httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

		ObjectMapper mapper = new ObjectMapper();
		
		resultMsg = new ResultMsg(ResultStatusCode.INVALID_TOKEN.getErrcode(), ResultStatusCode.INVALID_TOKEN.getErrmsg(), null);
		httpResponse.getWriter().write(mapper.writeValueAsString(resultMsg));
		return;
	}

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		
	}
}


7、在入口处注册filter

package com.xiaofangtech.sunt;

import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

import com.xiaofangtech.sunt.filter.HTTPBasicAuthorizeAttribute;
import com.xiaofangtech.sunt.filter.HTTPBearerAuthorizeAttribute;
import com.xiaofangtech.sunt.jwt.Audience;

@SpringBootApplication
@EnableConfigurationProperties(Audience.class)
public class SpringRestApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringRestApplication.class, args);
	}
	
	@Bean
    public FilterRegistrationBean  basicFilterRegistrationBean() {
		FilterRegistrationBean registrationBean = new FilterRegistrationBean();
		HTTPBasicAuthorizeAttribute httpBasicFilter = new HTTPBasicAuthorizeAttribute();
		registrationBean.setFilter(httpBasicFilter);
		List<String> urlPatterns = new ArrayList<String>();
	    urlPatterns.add("/user/getuser");
	    registrationBean.setUrlPatterns(urlPatterns);
	    return registrationBean;
    }
	
	@Bean
	public FilterRegistrationBean jwtFilterRegistrationBean(){
		FilterRegistrationBean registrationBean = new FilterRegistrationBean();
		HTTPBearerAuthorizeAttribute httpBearerFilter = new HTTPBearerAuthorizeAttribute();
		registrationBean.setFilter(httpBearerFilter);
		List<String> urlPatterns = new ArrayList<String>();
	    urlPatterns.add("/user/getusers");
	    registrationBean.setUrlPatterns(urlPatterns);
	    return registrationBean;
	}
}

8、添加获取md5的方法类MyUtils
package com.xiaofangtech.sunt.utils;

import java.security.MessageDigest;

public class MyUtils {
	public static String getMD5(String inStr) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            
            e.printStackTrace();
            return "";
        }
        char[] charArray = inStr.toCharArray();
        byte[] byteArray = new byte[charArray.length];
 
        for (int i = 0; i < charArray.length; i++)
            byteArray[i] = (byte) charArray[i];
 
        byte[] md5Bytes = md5.digest(byteArray);
 
        StringBuffer hexValue = new StringBuffer();
 
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16)
                hexValue.append("0");
            hexValue.append(Integer.toHexString(val));
        }
 
        return hexValue.toString();
    }
}

9、在返回信息类中补充添加错误码

        INVALID_CLIENTID(30003, "Invalid clientid"),
	INVALID_PASSWORD(30004, "User name or password is incorrect"),
	INVALID_CAPTCHA(30005, "Invalid captcha or captcha overdue"),
	INVALID_TOKEN(30006, "Invalid token");



10、代码中涉及的Audience类,在上一篇文章中定义,本文不再重复说明


11、代码整体结构



12、测试

1) 获取token,传入用户认证信息



    认证通过返回token信息




2) 使用上面获取的token进行接口调用

     未使用token,获取token错误,或者token过期时



    使用正确的token时



版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/sun_t89/article/details/51923017
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-07 22:24:00
  • 阅读 ( 989 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢