若依前后端分离版:增加新的登录接口,用于小程序或者APP获取token,并使用若依的验证方法

LoginController类

具体代码 
/**
     * app 登录
     */
    @AnonymousAccess
    @PostMapping("login")
    public AjaxResult login(@RequestBody LoginBody loginBody) {
        AjaxResult ajax = AjaxResult.success();
        // 生成令牌
        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword());
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }

登录校验 ——AppLoginService类

 具体代码

@Resource
private AppAuthenticationProvider authenticationManager;

  /**
     * 登录验证
     *
     * @param username 用户名
     * @param password 密码
     * @return 结果
     */
    public String login(String username, String password) {
        // 用户验证
        Authentication authentication;
        try {
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            } else {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        recordLoginInfo(loginUser.getUserId());
        // 生成token
        return tokenService.createToken(loginUser);
    }
AppAuthenticationProvider 类

 具体代码

@Component
public class AppAuthenticationProvider implements AuthenticationProvider {

    private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);

    @Autowired
    private AppUserDetailsServiceImpl userDetailsService;

    @SneakyThrows
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String userName = authentication.getName();// 这个获取表单输入中返回的用户名;
        Object password = authentication.getCredentials();//这个获取表单输入中返回的密码;

        // 这里构建来判断用户是否存在和密码是否正确
        UserDetails userInfo = userDetailsService.loadUserByUsername(userName); // 这里调用我们的自己写的获取用户的方法;

        if(!SecurityUtils.matchesPassword(password.toString(),userInfo.getPassword())){
            log.info("用户不存在/密码错误,{}", userName);
            throw new ServiceException("用户不存在/密码错误");
        }        

        Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
        // 构建返回的用户登录成功的token
        return new UsernamePasswordAuthenticationToken(userInfo, userInfo.getPassword(), authorities);
    }

    @Override
    public boolean supports(Class<?> authentication) {
//        return authentication.equals(UsernamePasswordAuthenticationToken.class);
        // 这里直接改成 return true;表示是支持这个执行
        return true;
    }

}
AppUserDetailsServiceImpl类

 具体代码

@Service
public class AppUserDetailsServiceImpl implements UserDetailsService {

    private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);

    @Autowired
    private IProductMemberService memberService;//自己写的接口

    @Autowired
    private IProductMemberCourtService memberCourtService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        ProductMember member = memberService.selectUserByUserName(username);//验证登录用户

        if (StringUtils.isNull(member)) {
            log.info("登录用户:{} 不存在.", username);
            throw new ServiceException("登录用户:" + username + " 不存在");
        } else if (UserStatus.DELETED.getCode().equals(member.getDelFlag())) {
            log.info("登录用户:{} 已被删除.", username);
            throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
        } else if (UserStatus.DISABLE.getCode().equals(member.getStatus())) {
            log.info("登录用户:{} 已被停用.", username);
            throw new ServiceException("对不起,您的账号:" + username + " 已停用");
        }

        return createLoginUser(member);
    }

    public UserDetails createLoginUser(ProductMember member) {
        return new LoginUser(member.getMemberId(), memberCourtService.selectCourtIdByMemberId(member.getMemberId()), member);
    }
}

此时运行时,会有冲突!!!

需要在 xxx-framework/src/main/java/....../SecurityConfig中条件

    @Qualifier("userDetailsServiceImpl")

如图:

 此时启动项目不会报冲突的错

千万千万要添加!!!

下图中的LongUser类要添加东西

 要在public String getPassword(){}中添加自己写的登录实体类的getPassword()

以上内容已经可以解决app和小程序新的登录接口方案和后台管理的登陆获取token不冲突

以下内容可作为参考:

登录认证JWTtoken验证机制

后端部分
/login 接口
userName
password
code 验证码
前端获取上面三个要素后调用接口,整体改接口做了下面几件事情

1、验证用户身份(账号密码+验证码)
2、生成token
3、保存用户登录态到spring security中

安全配置:定义了基本的配置信息
framework.config.SecurityConfig

UserDetailsServiceImpl 用户验证处理类

登录接口的服务类
framework.web.service.SysLoginService

JWT拦截器,拦截令牌并校验信息
framework.security.filter.JwtAuthenticationTokenFilter


详细过程

1、SysLoginService 中调用UserDetailsServiceImpl校验用户的密码是否匹配以及用户账户状态,校验通过后返回UserDetails实例,该实例包含了用户的基本信息和菜单权限信息
2、调用tokenService.createToken(loginUser)生成token
令牌生成的详细过程

生成uuid随机数,这个随机数用来做rediskey存储token
生成一个token(无时效)
拦截到的token如果距离失效在10分钟以内(可配置)就自动刷新有效期

前面提到了token本身无时效,有效期是通过redis控制的,因为jwt本身未提供刷新有效期的方法(可能是我不知道)。

以上用户调用了login接口并且获得了token

jwt令牌校验
 

/**
 * token过滤器 验证token有效性
 * 
 * @author sj
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
    @Autowired
    private TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException
    {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
        {
            tokenService.verifyToken(loginUser);
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        chain.doFilter(request, response);
    }
}


代码比较短,所以就直接贴出来,这段代码拦截了所有请求并且完成了令牌的校验和刷新,具体过程如下

1、tokenService.getLoginUser(request); 从request中获取token并校验,如果校验通过就返回LoginUser对象
2、校验LoginUser的token,如果再刷限期内就直接刷新
3、将LoginUser封装到SecurityContextHolder中作为全局的用户登录状态


注:第3条有两个好处

1、后续拦截器发现SecurityContextHolder中保存了用户时,就直接通过校验
2、通过SecurityContextHolder可以快速获取当前请求的登录信息。
以上基本上已经说名了JWT校验的基本过程,忽略了很多细节

getInfo 获取用户信息
1、用户的基本信息
2、用户所有的Permissions(菜单树)
3、用户所有的RopePersmission(roleKeys)

在这里插入图片描述
getRouters 获取前端页面路由信息
这个接口完全为前端准备,后面会专门讲述前端的权限控制

 在这里插入图片描述

  • 8
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
要实现前后端分离项目中的微信小程序登录接口,你需要完成以下步骤: 1. 在微信公众平台上,注册并创建一个小程序,并获得小程序AppID 和 AppSecret。 2. 在前端代码中编写小程序登录逻辑,获取用户的登录凭证 code,然后将 code 传递到后端。 3. 在后端代码中,使用传递过来的 code 调用微信提供的 API,获取用户的 OpenID 和 Session Key。 4. 后端将用户的 OpenID 和 Session Key 存储到数据库中,并生成一个 Token 返回给前端。 5. 前端Token 存储到本地缓存中,以便后续的请求中携带 Token。 6. 后续的请求中,前端需要在请求头中携带 Token,后端根据 Token 鉴权,判断用户是否登录。 以下是一个示例的后端代码(使用 Flask 框架实现): ```python from flask import Flask, request, jsonify import requests import json app = Flask(__name__) @app.route('/login', methods=['POST']) def login(): code = request.json['code'] appid = 'your_appid' secret = 'your_secret' url = f'https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={code}&grant_type=authorization_code' resp = requests.get(url) data = json.loads(resp.text) openid = data.get('openid') session_key = data.get('session_key') # 将 openid 和 session_key 存储到数据库中 # ... token = generate_token(openid) return jsonify({'token': token}) @app.route('/protected', methods=['GET']) def protected(): token = request.headers.get('Authorization') if verify_token(token): # 鉴权通过,执行业务逻辑 # ... return jsonify({'message': 'success'}) else: return jsonify({'message': 'unauthorized'}), 401 def generate_token(openid): # 生成 Token # ... return token def verify_token(token): # 验证 Token # ... return True ``` 在上述代码中,`/login` 接口用于处理小程序登录请求,`/protected` 接口用于处理需要鉴权的请求。你需要根据自己的业务逻辑进行相应的修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值