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


    LoginController类

    具体代码 
    
    1. /**
    2. * app 登录
    3. */
    4. @AnonymousAccess
    5. @PostMapping("login")
    6. public AjaxResult login(@RequestBody LoginBody loginBody) {
    7. AjaxResult ajax = AjaxResult.success();
    8. // 生成令牌
    9. String token = loginService.login(loginBody.getUsername(), loginBody.getPassword());
    10. ajax.put(Constants.TOKEN, token);
    11. return ajax;
    12. }

    登录校验 ——AppLoginService类

     具体代码

    1. @Resource
    2. private AppAuthenticationProvider authenticationManager;
    3.   /**
    4. * 登录验证
    5. *
    6. * @param username 用户名
    7. * @param password 密码
    8. * @return 结果
    9. */
    10. public String login(String username, String password) {
    11. // 用户验证
    12. Authentication authentication;
    13. try {
    14. // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
    15. authentication = authenticationManager
    16. .authenticate(new UsernamePasswordAuthenticationToken(username, password));
    17. } catch (Exception e) {
    18. if (e instanceof BadCredentialsException) {
    19. AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
    20. throw new UserPasswordNotMatchException();
    21. } else {
    22. AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
    23. throw new ServiceException(e.getMessage());
    24. }
    25. }
    26. AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
    27. LoginUser loginUser = (LoginUser) authentication.getPrincipal();
    28. recordLoginInfo(loginUser.getUserId());
    29. // 生成token
    30. return tokenService.createToken(loginUser);
    31. }
    AppAuthenticationProvider 类

     具体代码

    1. @Component
    2. public class AppAuthenticationProvider implements AuthenticationProvider {
    3. private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
    4. @Autowired
    5. private AppUserDetailsServiceImpl userDetailsService;
    6. @SneakyThrows
    7. @Override
    8. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    9. String userName = authentication.getName();// 这个获取表单输入中返回的用户名;
    10. Object password = authentication.getCredentials();//这个获取表单输入中返回的密码;
    11. // 这里构建来判断用户是否存在和密码是否正确
    12. UserDetails userInfo = userDetailsService.loadUserByUsername(userName); // 这里调用我们的自己写的获取用户的方法;
    13. if(!SecurityUtils.matchesPassword(password.toString(),userInfo.getPassword())){
    14. log.info("用户不存在/密码错误,{}", userName);
    15. throw new ServiceException("用户不存在/密码错误");
    16. }
    17. Collectionextends GrantedAuthority> authorities = userInfo.getAuthorities();
    18. // 构建返回的用户登录成功的token
    19. return new UsernamePasswordAuthenticationToken(userInfo, userInfo.getPassword(), authorities);
    20. }
    21. @Override
    22. public boolean supports(Class authentication) {
    23. // return authentication.equals(UsernamePasswordAuthenticationToken.class);
    24. // 这里直接改成 return true;表示是支持这个执行
    25. return true;
    26. }
    27. }
    AppUserDetailsServiceImpl类

     具体代码

    1. @Service
    2. public class AppUserDetailsServiceImpl implements UserDetailsService {
    3. private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
    4. @Autowired
    5. private IProductMemberService memberService;//自己写的接口
    6. @Autowired
    7. private IProductMemberCourtService memberCourtService;
    8. @Override
    9. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    10. ProductMember member = memberService.selectUserByUserName(username);//验证登录用户
    11. if (StringUtils.isNull(member)) {
    12. log.info("登录用户:{} 不存在.", username);
    13. throw new ServiceException("登录用户:" + username + " 不存在");
    14. } else if (UserStatus.DELETED.getCode().equals(member.getDelFlag())) {
    15. log.info("登录用户:{} 已被删除.", username);
    16. throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
    17. } else if (UserStatus.DISABLE.getCode().equals(member.getStatus())) {
    18. log.info("登录用户:{} 已被停用.", username);
    19. throw new ServiceException("对不起,您的账号:" + username + " 已停用");
    20. }
    21. return createLoginUser(member);
    22. }
    23. public UserDetails createLoginUser(ProductMember member) {
    24. return new LoginUser(member.getMemberId(), memberCourtService.selectCourtIdByMemberId(member.getMemberId()), member);
    25. }
    26. }

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

    需要在 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中

    1. 安全配置:定义了基本的配置信息
    2. framework.config.SecurityConfig
    3. UserDetailsServiceImpl 用户验证处理类
    4. 登录接口的服务类
    5. framework.web.service.SysLoginService
    6. JWT拦截器,拦截令牌并校验信息
    7. 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令牌校验
     

    1. /**
    2.  * token过滤器 验证token有效性
    3.  * 
    4.  * @author sj
    5.  */
    6. @Component
    7. public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
    8. {
    9.     @Autowired
    10.     private TokenService tokenService;
    11.     @Override
    12.     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
    13.             throws ServletException, IOException
    14.     {
    15.         LoginUser loginUser = tokenService.getLoginUser(request);
    16.         if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
    17.         {
    18.             tokenService.verifyToken(loginUser);
    19.             UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
    20.             authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
    21.             SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    22.         }
    23.         chain.doFilter(request, response);
    24.     }
    25. }


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

    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 获取前端页面路由信息
    这个接口完全为前端准备,后面会专门讲述前端的权限控制

     在这里插入图片描述

  • 相关阅读:
    Springboot启动流程分析(一):IOC容器初始化
    ReentrantReadWriteLock读写锁实现原理详解
    python
    齐鲁大地市省学馆《乡村振兴战略下传统村落文化旅游设计》山东图书香
    对进程、线程和协程的理解以及它们的区别
    C++:回调函数的应用
    [linux] 把txt文本文件分成10个子文件,并保存。 linux命令
    pytorch中nn.functional和nn.Module的区别
    人工智能基础_机器学习046_OVR模型多分类器的使用_逻辑回归OVR建模与概率预测---人工智能工作笔记0086
    Leetcode—260.只出现一次的数字III【中等】
  • 原文地址:https://blog.csdn.net/yyongsheng/article/details/127584458