为了账号安全,请及时绑定邮箱和手机立即绑定

认证与鉴权(2)

标签:
Java

应用小程序验证工具包

https://img1.sycdn.imooc.com/610e6ee2000175a708320214.jpg

<!--        微信小程序API,可以实现微信登录,微信支付等功能-->
        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-miniapp</artifactId>
            <version>3.5.0</version>
        </dependency>

新建配置类WxConfiguration

package com.itmuch.usercenter.configuration;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;

@Configurable
public class WxConfiguration {
    @Bean
    public WxMaConfig wxMaConfig(){
        WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
        config.setAppid("wx48ecafb715371267");
        config.setSecret("1644c28011c189f6186a1bc4f9205b6b");
        return config;
    }

    @Bean
    public WxMaService wxMaService(WxMaConfig wxMaConfig){
        WxMaServiceImpl service = new WxMaServiceImpl();
        service.setWxMaConfig(wxMaConfig);
        return service;
    }
}

在控制器中就可以使用了

package com.itmuch.usercenter.controller.user;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.alibaba.csp.ahas.shaded.com.alibaba.acm.shaded.com.google.common.collect.Maps;
import com.itmuch.usercenter.auth.CheckLogin;
import com.itmuch.usercenter.domain.dto.user.JwtTokenRespDto;
import com.itmuch.usercenter.domain.dto.user.LoginRespDto;
import com.itmuch.usercenter.domain.dto.user.UserLoginDto;
import com.itmuch.usercenter.domain.dto.user.UserRespDto;
import com.itmuch.usercenter.domain.entity.user.User;
import com.itmuch.usercenter.service.user.UserService;
import com.itmuch.usercenter.util.JwtOperator;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/users")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    @Resource
    private WxMaService wxMaService;

    @Autowired
    private JwtOperator jwtOperatorl;

    @GetMapping("/{id}")
    @CheckLogin
    public User findById(@PathVariable Integer id) {
        log.info("我被请求了");
        return this.userService.findUserById(id);
    }

    /**
     * 模拟生成token
     * @return
     */
    @GetMapping("/gen-token")
    public String genToken(){
        HashMap<String, Object> objectObjectHashMap = Maps.newHashMap();
        objectObjectHashMap.put("id", "1");
        objectObjectHashMap.put("wxNickname", "fox");
        objectObjectHashMap.put("role", "user");
        return this.jwtOperatorl.generateToken(objectObjectHashMap);
    }

    @PostMapping("/login")
    public LoginRespDto login(@RequestBody UserLoginDto userLoginDto) throws WxErrorException {
        //微信小程序登陆的结果
        WxMaJscode2SessionResult result = this.wxMaService.getUserService().getSessionInfo(userLoginDto.getCode());

        //微信的openId,用户在微信上的唯一标识
        String openId = result.getOpenid();

        //查看用户是否已经注册,如果没有注册就注册(插入user表)
        //如果已经注册
        User user = this.userService.login(userLoginDto, openId);
        //就颁发token
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("id", user.getId());
        userInfo.put("wxNickName", user.getWxNickname());
        userInfo.put("role", user.getRoles());

        String token = jwtOperatorl.generateToken(userInfo);

        log.info(
                "用户{}登录成功,生成token = {} ,有效期为{}",
                userLoginDto.getWxNickname(),
                token,
                jwtOperatorl.getExpirationTime()
        );

        //构建响应
        return LoginRespDto.builder()
                .userRespDto(
                        UserRespDto.builder()
                                .id(user.getId())
                                .avatarUrl(user.getAvatarUrl())
                                .bonus(user.getBonus())
                                .wxNickname(user.getWxNickname())
                                .build()
                )
                .jwtTokenRespDto(
                        JwtTokenRespDto.builder()
                                .expirationTime(jwtOperatorl.getExpirationTime().getTime())
                                .token(token)
                                .build()
                )
                .build();
    }
}

在findById加上自定义注解@checklogin

@checklogin利用切面进行设置

先定义一个注解

package com.itmuch.usercenter.auth;

public @interface CheckLogin {
}

@checklogin利用切面进行设置

package com.itmuch.usercenter.auth;

import com.itmuch.usercenter.util.JwtOperator;
import io.jsonwebtoken.Claims;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

@Aspect
@Component
public class CheckLoginAspect {

    @Resource
    private JwtOperator jwtOperator;

    @Around("@annotation(com.itmuch.usercenter.auth.CheckLogin)")//只要加了checklogin注解的都会走这个方法
    public Object checkLogin(ProceedingJoinPoint point)   {
        try {
            //从header中获取token
            //利用静态方法获取request
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = attributes.getRequest();

            String token = request.getHeader("X-Token");

            //检查token是否合法,是否过期,如果不通过直接抛异常,否则放行
            Boolean isVaild = jwtOperator.validateToken(token);
            if (!isVaild){
                throw new SecurityException("token不合法或过期");
            }

            //检查通过,将用户信息放进request的attribute中
            Claims claims = jwtOperator.getClaimsFromToken(token);
            request.setAttribute("id",claims.get("id"));
            request.setAttribute("wxNickname",claims.get("wxNickname"));
            request.setAttribute("role",claims.get("role"));


            //放行
            return point.proceed();
        }catch (Throwable throwable){
            throw new SecurityException("token不合法或过期");
        }
    }
}

设置自定义异常

package com.itmuch.usercenter.security;

public class SecurityException extends RuntimeException{
}

设置自定义异常捕捉器

package com.itmuch.usercenter.auth;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

//统一异常捕捉器
@RestControllerAdvice
@Slf4j
public class GlobalExceptionErrorHandler {

    @ExceptionHandler(SecurityException.class)
    public ResponseEntity<ErrorBody> error(SecurityException e) {
        log.warn("发生securityException异常",e);
        ResponseEntity<ErrorBody> errorBodyResponseEntity = new ResponseEntity<>(
                ErrorBody.builder()
                        .body("Token非法,用户不允许访问")
                        .status(HttpStatus.UNAUTHORIZED.value())
                        .build(),
                HttpStatus.UNAUTHORIZED
        );
        return errorBodyResponseEntity;
    }
}

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
class ErrorBody {
    private Integer status;
    private String body;
}

总结:在访问192.168.0.100:8003/users/1的时候需要携带X-Token,不然会报错

同样将文件复制到内容中心,在/share/{id}接口上打上

@CheckLogin

注解,等携带X-Token访问时会报错,是因为内容中心的token没有传递到用户中心,一下是服务之间传递token的方式:

feign传递token(@RequestHeader)

package com.itmuch.content.controller.content;

import com.itmuch.content.auth.CheckLogin;
import com.itmuch.content.domain.dto.content.ShareDTO;
import com.itmuch.content.service.content.ShareService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@RestController
@RequestMapping("/share")
public class ShareController {

    @Resource
    private ShareService shareService;

    @GetMapping("/{id}")
    @CheckLogin
    public ShareDTO findById(@PathVariable Integer id, @RequestHeader("X-Token") String token){

        return this.shareService.findById(id,token);
    }
}

加上@RequestHeader注解,将请求头的X-Token注入到token,修改相对应的代码逻辑

https://img1.sycdn.imooc.com//6113f15a0001330d07370169.jpg

https://img1.sycdn.imooc.com//6113f1690001447309220177.jpg

package com.itmuch.content.feignClient;

import com.itmuch.content.domain.dto.user.UserDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;

//@FeignClient(name = "user-center",configuration = UserCenterFeignConfiguration.class)
@FeignClient(name = "user-center")
//        fallback = UserCenterFeignClientFallback.class,
 //       fallbackFactory = UserCenterFeignClientFallbackFactory.class)
//用了fallbackFactory就不要用fallback,fallbackFactory会比fallback更加强大,可以返回错误信息;
public interface UserCenterFeignClient {
    /**
     * 当调用findById时会构造http://user-center/users/{id}
     *
     * @param id
     * @return
     */
    @GetMapping("/users/{id}")
    UserDTO findUserById(@PathVariable Integer id,@RequestHeader("X-Token") String token);
}

优点:操作简单

缺点:现在修改一个API就要修改这么多文件,如果很多API这样实现起来就很不现实


feign的拦截器(RequestInterceptor)

package com.itmuch.content.feignClient.interceptor;

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

public class TokenRelayRequestIntecepor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        //1、获取token
        //利用静态方法获取request
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = attributes.getRequest();
        String token = request.getHeader("X-Token");

        //2、传递token
        //把指定的值放进name里面
        if (StringUtils.isNotEmpty(token)) {
            requestTemplate.header("X-Token",token);
        }
    }
}

使用拦截器不需要改变其他代码,

配置拦截器有两种方式,可以在@feignclient中使用configuration,或者直接在配置文件中进行全局配置

https://img1.sycdn.imooc.com//6113f8150001184611040357.jpg



点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消