一、使用流程
1、pom文件引入
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2、註解聲明
一個用於驗證token登錄的,一個用於獲取用戶信息的
/**
* app登錄效驗
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Login {
}
/**
* 登錄用戶信息
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {
}
3、工具類
/**
* jwt工具類
*/
@ConfigurationProperties(prefix = "newpay.jwt")
@Component
public class JwtUtils {
private Logger logger = LoggerFactory.getLogger(getClass());
private String secret;
private long expire;
private String header;
/**
* 生成jwt token
*/
public String generateToken(long userId) {
Date nowDate = new Date();
//過期時間
Date expireDate = new Date(nowDate.getTime() + expire * 1000);
return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setSubject(userId+"")
.setIssuedAt(nowDate)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Claims getClaimByToken(String token) {
try {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}catch (Exception e){
logger.debug("validate is token error ", e);
return null;
}
}
/**
* token是否過期
* @return true:過期
*/
public boolean isTokenExpired(Date expiration) {
return expiration.before(new Date());
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public long getExpire() {
return expire;
}
public void setExpire(long expire) {
this.expire = expire;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
}
對應的配置文件:
newpay:
# APP模塊,是通過jwt認證的,如果要使用APP模塊,則需要修改【加密祕鑰】
jwt:
# 加密祕鑰
secret: 123456
# token有效時長,7天,單位秒
expire: 604800
# 請求頭名
header: token
4、攔截器
/**
* 權限(Token)驗證
*/
@Component
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
@Autowired
private JwtUtils jwtUtils;
public static final String USER_KEY = "userId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Login annotation;
if(handler instanceof HandlerMethod) {
annotation = ((HandlerMethod) handler).getMethodAnnotation(Login.class);
}else{
return true;
}
if(annotation == null){
return true;
}
//獲取用戶憑證
String token = request.getHeader(jwtUtils.getHeader());
if(StringUtils.isBlank(token)){
token = request.getParameter(jwtUtils.getHeader());
}
//憑證爲空
if(StringUtils.isBlank(token)){
throw new AppBizException(jwtUtils.getHeader() + "不能爲空", HttpStatus.UNAUTHORIZED.value());
}
Claims claims = jwtUtils.getClaimByToken(token);
if(claims == null || jwtUtils.isTokenExpired(claims.getExpiration())){
throw new AppBizException(jwtUtils.getHeader() + "失效,請重新登錄", HttpStatus.UNAUTHORIZED.value());
}
//設置userId到request裏,後續根據userId,獲取用戶信息
request.setAttribute(USER_KEY, Long.parseLong(claims.getSubject()));
return true;
}
}
5、用戶解析器
/**
* 有@LoginUser註解的方法參數,注入當前登錄用戶
*/
@Component
public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Autowired
private UserService userService;
//針對LoginUser註解,獲取UserEntity實體類
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().isAssignableFrom(UserEntity.class) && parameter.hasParameterAnnotation(LoginUser.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
NativeWebRequest request, WebDataBinderFactory factory) throws Exception {
//獲取用戶ID
Object object = request.getAttribute(AuthorizationInterceptor.USER_KEY, RequestAttributes.SCOPE_REQUEST);
if(object == null){
return null;
}
//獲取用戶信息
UserEntity user = userService.selectById((Long)object);
return user;
}
}
6、mvc配置
/**
* MVC配置
*
* @author wwa
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private AuthorizationInterceptor authorizationInterceptor;
@Autowired
private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver;
@Autowired
private LoginMemberHandlerMethodArgumentResolver loginMemberHandlerMethodArgumentResolver;
//添加過濾路徑
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authorizationInterceptor).addPathPatterns("/app/**");
registry.addInterceptor(authorizationInterceptor).addPathPatterns("/wx/**");
}
//添加解析器
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(loginUserHandlerMethodArgumentResolver);
argumentResolvers.add(loginMemberHandlerMethodArgumentResolver);
}
}
7、token生成
package com.shouft.newpay.modules.app.controller;
import com.shouft.newpay.common.utils.Resp;
import com.shouft.newpay.common.validator.ValidatorUtils;
import com.shouft.newpay.modules.app.form.LoginForm;
import com.shouft.newpay.modules.app.service.UserService;
import com.shouft.newpay.modules.app.utils.JwtUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* APP登錄授權
*/
@RestController
@RequestMapping("/app")
public class AppLoginController {
@Autowired
private UserService userService;
@Autowired
private JwtUtils jwtUtils;
@PostMapping("login")
public Resp login(@RequestBody LoginForm form){
//用戶登錄
long userId = userService.login(form);
//生成token
String token = jwtUtils.generateToken(userId);
Map<String, Object> map = new HashMap<>(1);
map.put("token", token);
// map.put("data","");
return Resp.ok(map);
}
}
8、註解過濾,獲取用戶
添加過濾路徑
/**
* APP測試接口
*/
@RestController
@RequestMapping("/app")
public class AppTestController {
@Login
@GetMapping("userInfo")
public Resp userInfo(@LoginUser UserEntity user){
System.out.println("成功獲取用戶信息:"+user);
return Resp.ok().put("user", user);
}
@Login
@GetMapping("userId")
public Resp userInfo(@RequestAttribute("userId") Integer userId){
System.out.println("攜帶token訪問成功"+userId);
return Resp.ok().put("userId", userId);
}
@GetMapping("notToken")
public Resp notToken(){
return Resp.ok().put("msg", "無需token也能訪問。。。");
}
}
9、配合Shiro
@Configuration
public class ShiroConfig {
...
@Bean("shiroFilter")
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
//oauth過濾
Map<String, Filter> filters = new HashMap<>(1);
filters.put("oauth2", new Oauth2Filter());
shiroFilter.setFilters(filters);
Map<String, String> filterMap = new LinkedHashMap<>();
//對應的app和wx路徑
filterMap.put("/app/**", "anon");
filterMap.put("/wx/**", "anon");
...
filterMap.put("/**", "oauth2");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
10、小程序使用
//登錄獲取token
requestLogin: function(code,e) {
wx.request({
url: api.AuthLoginByWeixin,
data: {
code: code,
userInfo: e.detail.userInfo
},
method: 'POST',
header: {
'content-type': 'application/json'
},
success: function(res) {
if (res.data.code == 0) {
wx.setStorageSync('token', res.data.token);
}
});
},
//獲取用戶信息,訪問@LoginUser
wx.request({
url: api.UserInfo,
data: {},
method: 'GET',
header:{
'token': wx.getStorageSync('token')
},
success: function (res) {
if (res.data.code == 0) {
console.log(res.data.user);
}