Yshop框架的小程序登录

目录

1配置

2.登录

3.过滤器

4. 工具类完善

5. 获取当前用户数据的工具


1配置

根据请求头去判断,走小程序,还是Pc端。

#jwt
jwt:
  header: Authorization
  #小程序前缀 请求头
  mini-program-header: MiAuthorization
  # 令牌前缀
  token-start-with: Bearer
  secret: k09BQnaF
  # 必须使用最少88位的Base64对该令牌进行编码
  base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI=
  # 令牌过期时间 此处单位/毫秒 ,默认4小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html
  token-validity-in-seconds: 14400000
  # 在线用户key
  online-key: online-token
  # 小程序在线用户
  mi-online-key: mi-online-token
  # 验证码
  code-key: code-key

2.登录

登录时,设置用户信息存储到redis中。

    /***
     * 根据账户,密码 登录
     * @param exUser
     * @return
     */
    @Override
    public R<Object> VxLogin(ExUser exUser) {

        // 查询数据库中的账号密码是否存在
        ExUser exUserA = exUserMapper.selectOne(
                new LambdaQueryWrapper<ExUser>()
                        .eq(StringUtils.isNotBlank(exUser.getUserUsername()), ExUser::getUserUsername, exUser.getUserUsername())
                        .eq(StringUtils.isNotBlank(exUser.getUserPassword()), ExUser::getUserPassword, exUser.getUserPassword())
                        .eq(Objects.nonNull(exUser.getUserStatus()), ExUser::getUserStatus, 1)
        );
        if (Objects.isNull(exUserA)) {
            return R.error("该用户未存在");
        }

        // 生成token
        String token = tokenUtil.generateTokenA(exUserA);

        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", properties.getTokenStartWith() + token);
            put("user", exUserA);
        }};
        RedisUtil.set(properties.getMiOnlineKey() + token, exUserA, properties.getTokenValidityInSeconds() / 1000);



        return R.success(authInfo);
    }

3.过滤器

获取请求头,判断是小程序接口还是Pc端接口。

/**
 * Copyright (C) 2018-2022
 * All rights reserved, Designed By www.yixiang.co
 */
package co.yixiang.modules.security.security;

import co.yixiang.domain.ExUser;
import co.yixiang.modules.security.config.SecurityProperties;
import co.yixiang.modules.security.service.OnlineUserService;
import co.yixiang.modules.user.vo.OnlineUser;
import co.yixiang.utils.SpringContextHolder;
import co.yixiang.utils.StringUtils;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author /
 */
@Slf4j
public class TokenFilter extends GenericFilterBean {

    @Autowired
    private SecurityProperties securityProperties;

    private final TokenUtil tokenUtil;


    TokenFilter(TokenUtil tokenUtil) {
        this.tokenUtil = tokenUtil;
    }



    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

        String requestRri = httpServletRequest.getRequestURI();

        OnlineUser onlineUser = null;
        ExUser exUser = null;
        String authToken = null;
        String authTokenA = null;

        try {
            SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class);
            OnlineUserService onlineUserService = SpringContextHolder.getBean(OnlineUserService.class);

            String clientType = httpServletRequest.getHeader(properties.getHeader());
            String miniProgramClientType = httpServletRequest.getHeader(properties.getMiniProgramHeader());

            // 检查是否存在PC端或小程序端的请求头
            if (clientType == null && miniProgramClientType == null) {
                log.error("Both Client-Type and Mini-Program-Client-Type headers are missing.");
                filterChain.doFilter(httpServletRequest, servletResponse);
                return;
            }

            if (StringUtils.isNotBlank(clientType)) {
                // PC端
                authToken = tokenUtil.getToken(httpServletRequest);
            } else if (StringUtils.isNotBlank(miniProgramClientType)) {
                // 小程序
                authTokenA = tokenUtil.getTokenA(httpServletRequest);
            }

            if (authToken == null && authTokenA == null) {
                log.error("Both authToken and authTokenA are null.");
                filterChain.doFilter(httpServletRequest, servletResponse);
                return;
            }

            if (StringUtils.isNotBlank(authToken)) {
                onlineUser = onlineUserService.getOne(properties.getOnlineKey() + authToken);
            } else if (StringUtils.isNotBlank(authTokenA)) {
                exUser = onlineUserService.getOneA(properties.getMiOnlineKey() + authTokenA);
//                String userJson = RedisUtil.get("userA");
//                ExUser user = JSON.parseObject(userJson, ExUser.class);
            }

        } catch (ExpiredJwtException e) {
            log.error(e.getMessage());
        }


     // Pc
        String username = StringUtils.isNotBlank(authToken) ? tokenUtil.getUsernameFromToken(authToken) : null;
        // 小程序
        String usernameA = StringUtils.isNotBlank(authTokenA) ? tokenUtil.getUsernameFromToken(authTokenA) : null;

        if (onlineUser != null && username != null && SecurityContextHolder.getContext().getAuthentication() == null && tokenUtil.validateToken(authToken)) {
            UserDetails userDetails = tokenUtil.getUserDetails(authToken);
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
            SecurityContextHolder.getContext().setAuthentication(authentication);
            log.debug("set Authentication to security context for '{}', uri: {}", authentication.getName(), requestRri);
        }else if(exUser != null && usernameA != null && SecurityContextHolder.getContext().getAuthentication() == null && tokenUtil.validateTokenA(authTokenA)){
            UserDetails userDetailsA = tokenUtil.getUserDetailsA(authTokenA);
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetailsA, null, null);
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }else {
            tokenUtil.removeToken(authToken);
            tokenUtil.removeToken(authTokenA);
            log.debug("no valid JWT token found, uri: {}", requestRri);
        }


        filterChain.doFilter(httpServletRequest, servletResponse);
    }
}

4. 工具类完善


package co.yixiang.modules.security.security;

import co.yixiang.domain.ExUser;
import co.yixiang.modules.security.config.SecurityProperties;
import co.yixiang.modules.security.security.vo.JwtUser;
import co.yixiang.utils.RedisUtils;
import co.yixiang.utils.StringUtils;
import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Base64;
import java.util.Date;

/**
 * Token 工具类
 *
 * @author lioncity
 */

@Component
public class TokenUtil {
    @Autowired
    private SecurityProperties properties;

    /**
     * Logger
     */

    protected static final Logger LOGGER = LoggerFactory.getLogger(TokenUtil.class);


    /**
     * 权限缓存前缀
     */
    private static final String REDIS_PREFIX_AUTH = "auth:";

    /**
     * 小程序权限缓存前缀
     */
    private static final String REDIS_PREFIX_AUTH_A = "mi-auth:";


    /**
     * 用户信息缓存前缀
     */
    private static final String REDIS_PREFIX_USER = "user-details:";

    /**
     * 小程序 用户信息缓存前缀
     */
    private static final String REDIS_PREFIX_USER_A = "mi-user-details:";

    /**
     * redis repository
     */
    @Autowired
    private RedisUtils redisUtils;

    /**
     * 获取用户名
     *
     * @param token Token
     * @return String
     */

    public String getUsernameFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims != null ? claims.getSubject() : null;
    }

    /**
     * 获取过期时间
     *
     * @param token Token
     * @return Date
     */

    public Date getExpiredFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims != null ? claims.getExpiration() : null;
    }

    /**
     * 获得 Claims
     *
     * @param token Token
     * @return Claims
     */

    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(properties.getSecret())
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            LOGGER.warn("getClaimsFromToken exception", e);
            claims = null;
        }
        return claims;
    }

    /**
     * 计算过期时间
     *
     * @return Date
     */
    private Date generateExpired() {
        return new Date(System.currentTimeMillis() + properties.getTokenValidityInSeconds() * 1000);
    }

    /**
     * 判断 Token 是否过期
     *
     * @param token Token
     * @return Boolean
     */

    private Boolean isTokenExpired(String token) {
        Date expirationDate = getExpiredFromToken(token);
        return expirationDate.before(new Date());
    }

    /**
     * 生成 Token
     *
     * @param userDetails 用户信息
     * @return String
     */

    public String generateToken(UserDetails userDetails) {
        String secret=properties.getSecret();
        String token = Jwts.builder()
                .setSubject(userDetails.getUsername())
                .setExpiration(generateExpired())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();

        String key = REDIS_PREFIX_AUTH + userDetails.getUsername() + ":" + token;
        redisUtils.set(key, token, properties.getTokenValidityInSeconds() / 1000);
        putUserDetails(userDetails);
        return token;
    }

    public String generateTokenA(ExUser exUser) {
        String secret=properties.getSecret();
        String token = Jwts.builder()
                .setSubject(exUser.getUserUsername())
                .setExpiration(generateExpired())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();

        String key = REDIS_PREFIX_AUTH_A + exUser.getUserUsername() + ":" + token;
        redisUtils.set(key, token, properties.getTokenValidityInSeconds() / 1000);
        putUserDetailsA(exUser);
        return token;
    }

    /**
     * 验证 Token
     *
     * @param token Token
     * @return Boolean
     */

    public Boolean validateToken(String token) {
        final String username = getUsernameFromToken(token);
        String key = REDIS_PREFIX_AUTH + username+ ":" + token;
        Object data = redisUtils.get(key);
        String redisToken = data == null ? null : data.toString();
        return StringUtils.isNotEmpty(token) && !isTokenExpired(token) && token.equals(redisToken);
    }

    /**
     * 验证 Token
     *
     * @param token Token
     * @return Boolean
     */

    public Boolean validateTokenA(String token) {
        final String username = getUsernameFromToken(token);
        String key = REDIS_PREFIX_AUTH_A + username+ ":" + token;
        Object data = redisUtils.get(key);
        String redisToken = data == null ? null : data.toString();
        return StringUtils.isNotEmpty(token) && !isTokenExpired(token) && token.equals(redisToken);
    }

    /**
     * 移除 Token
     *
     * @param token Token
     */

    public void removeToken(String token) {
        final String username = getUsernameFromToken(token);
        String key = REDIS_PREFIX_AUTH + username+ ":" + token;
        redisUtils.del(key);
        delUserDetails(username);
    }

    /**
     * 获得用户信息 Json 字符串
     *
     * @param token Token
     * @return String
     */

    protected String getUserDetailsString(String token) {
        final String username = getUsernameFromToken(token);
        String key = REDIS_PREFIX_USER + username;
        Object data = redisUtils.get(key);
        return data == null ? null : data.toString();
    }

    /**
     *  小程序 获得用户信息 Json 字符串
     *
     * @param token Token
     * @return String
     */

    protected String getUserDetailsStringA(String token) {
        final String username = getUsernameFromToken(token);
        String key = REDIS_PREFIX_USER_A + username;
        Object data = redisUtils.get(key);
        return data == null ? null : data.toString();
    }

    /**
     * 获得用户信息
     *
     * @param token Token
     * @return UserDetails
     */

    public UserDetails getUserDetails(String token) {
        String userDetailsString = getUserDetailsString(token);
        if (userDetailsString != null) {
            return new Gson().fromJson(userDetailsString, JwtUser.class);
        }
        return null;
    }

    /**
     * 小程序 获得用户信息
     *
     * @param token Token
     * @return UserDetails
     */

    public UserDetails getUserDetailsA(String token) {
        String userDetailsString = getUserDetailsStringA(token);
        if (userDetailsString != null) {
            return new Gson().fromJson(userDetailsString, ExUser.class);
        }
        return null;
    }


    /**
     * 存储用户信息
     *
     * @param userDetails 用户信息
     */

    private void putUserDetails(UserDetails userDetails) {
        String key = REDIS_PREFIX_USER + userDetails.getUsername();
        redisUtils.set(key, new Gson().toJson(userDetails), properties.getTokenValidityInSeconds() / 1000);
    }


    private void putUserDetailsA(ExUser exUser) {
        String key = REDIS_PREFIX_USER_A + exUser.getUserUsername();
        redisUtils.set(key, new Gson().toJson(exUser), properties.getTokenValidityInSeconds() / 1000);
    }


    /**
     * 删除用户信息
     *
     * @param username 用户名
     */

    private void delUserDetails(String username) {
        String key = REDIS_PREFIX_USER + username;
        redisUtils.del(key);
    }

    public String getToken(HttpServletRequest request) {
        final String requestHeader = request.getHeader(properties.getHeader());
        if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) {
            return requestHeader.substring(7);
        }
        return null;
    }

    public String getTokenA(HttpServletRequest request) {
        final String requestHeader = request.getHeader(properties.getMiniProgramHeader());
        if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) {
            return requestHeader.substring(7);
        }
        return null;
    }

    public static void main(String[] args) {
        String key = Base64.getEncoder().encodeToString("123".getBytes());
        Claims claims = Jwts.parser().setSigningKey(key)
                .parseClaimsJws("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFwcCIsIndyaXRlIl0sInVpbiI6MSwiZXhwIjoxNTc1MDE1ODgzLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiYjdiYjQ1NTQtNTQ4OS00YTg5LWI3NjQtNzNjODI0YzljNGMyIiwiY2xpZW50X2lkIjoibHZoYWliYW8ifQ.x7QZxRAR1wuX_YNLi6EzRJ1iaKr1rIEUgjtYF0oSx5k").getBody();
        System.out.println(JSON.toJSONString(claims));
    }
}

5. 获取当前用户数据的工具

判断该接口是否被类实现

/**
 * Copyright (C) 2018-2022
 * All rights reserved, Designed By www.yixiang.co

 */
package co.yixiang.utils;

import cn.hutool.json.JSONObject;
import co.yixiang.domain.ExUser;
import co.yixiang.exception.BadRequestException;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;

/**
 * 获取当前登录的用户
 * @author Zheng Jie
 * @date 2019-01-17
 */
public class SecurityUtils {

    public static UserDetails getUserDetails() {
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期");
        }

        if(authentication.getPrincipal() instanceof ExUser){
            return (UserDetails) authentication.getPrincipal();
        }

        if (authentication.getPrincipal() instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
            UserDetailsService userDetailsService = SpringContextHolder.getBean(UserDetailsService.class);
            return userDetailsService.loadUserByUsername(userDetails.getUsername());
        }
        throw new BadRequestException(HttpStatus.UNAUTHORIZED, "找不到当前登录的信息");
    }

    /**
     * 获取系统用户名称
     * @return 系统用户名称
     */
    public static String getUsername(){
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期");
        }
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        return userDetails.getUsername();
    }

    /**
     * 获取系统用户id
     * @return 系统用户id
     */
    public static Long getUserId(){
        Object obj = getUserDetails();
        JSONObject json = new JSONObject(obj);
        return json.get("id", Long.class);
    }
}

yshop基于当前流行技术组合的前后端分离商城系统: SpringBoot2+MybatisPlus+SpringSecurity+jwt+redis+Vue的前后端分离的商城系统, 包含商城、拼团、砍价、商户管理、 秒杀、优惠券、积分、分销、会员、充值、多门店等功能,更适合企业或个人二次开发。 功能: 一、商品模块:商品添加、规格设置,商品上下架等 二、订单模块:下单、购物车、支付,发货、收货、评价、退款等 三、营销模块:积分、优惠券、分销、砍价、拼团、秒杀、多门店等 四、微信模块:自定义菜单、自动回复、微信授权、图文管理、模板消息推送 五、配置模块:各种配置 六、用户模块:登陆、注册、会员卡、充值等 七、其他等 项目结构: 项目采用分模块开发方式 yshop-weixin 微信相关模块 yshop-common 公共模块 yshop-admin 后台模块 yshop-logging 日志模块 yshop-tools 第三方工具模块 yshop-generator 代码生成模块 yshop-shop 商城模块 yshop-mproot mybatisPlus docker部署: 1、创建一个存储第三方软件服务Docker Compose文件目录:      mkdir -p /yshop/soft 2、然后在该目录下新建一个docker-compose.yml文件:     vim /yshop/soft/docker-compose.yml 3、接着创建上面docker-compose.yml里定义的挂载目录:     mkdir -p /yshop/mysql/data /yshop/redis/data /yshop/redis/conf 4、创建Redis配置文件redis.conf:     touch /yshop/redis/conf/redis.conf 5、docker 部署参考根目录docker文件夹 6、以上创建好之后参考docker下文件,先执行软件安装:   cd /yshop/soft   docker-compose up -d  启动   docker ps -a 查看镜像 7、运行docker/applicatiion目录下 docker-compose,当然之前一定要打包jar包,构建镜像 切换到Dockerfile 文件下: docker build -t yshop-admin .   3.2.1版本已经正式发布啦!: 1、秒杀列表与详情页面UI优化 2、拼团商品详情UI优化 3、优惠券列表UI优化 4、修复小程序官方登陆升级调整的问题 5、放开商品详情必须要登陆才能查看的权限 6、拼团列表新增浏览数与访客数 6、修复收藏的问题 7、修复退款问题 8、修复Email配置问题 9、修复积分支付0的问题 10、修复APP充值问题 11、其他等修复优化,详情请看git commit提交记录
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值