JWT的app/小程序中使用

一、使用流程

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);
      }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章