前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >springboot第21集:SSO

springboot第21集:SSO

作者头像
达达前端
发布2023-10-08 18:26:32
2010
发布2023-10-08 18:26:32
举报
文章被收录于专栏:达达前端达达前端
  • 单点登录
  • 单点登出
  • 支持跨域单点登录
  • 支持跨域单点登出
1fb43874fe157f870b6096e4868ebe89.png
1fb43874fe157f870b6096e4868ebe89.png

前台站点:业务站点A,业务站点B

SSO站点:登录,退出

SSO服务:登录,登录状态,登出

数据库,登录状态缓存在Redis

  • 登录时序图

客户端,访问需要登录的页面,从业务站点,如果未从Cookie中获取AuthToken,跳转到登录页面,访问SSO站点,提交用户名,密码,验证用户登录的 SSO 服务,访问DB验证账号,保存登录状态 Redis,返回成功Redis,返回AuthToken,将AuthToken放入Cookie中 domian = test.com 返回 302:跳转到页面,将AuthToken保存到Cookie中,domain = test.com,跳转到访问的页面

  • 登录完成之后通过回调的方式,将AuthToken传递给主域名之外的站点,该站点自行将AuthToken保存在当前域下的Cookie中。
  • 登出完成之后通过回调的方式,调用非主域名站点的登出页面,完成设置Cookie中的AuthToken过期的操作。

这个错误提示表明,代码尝试从ServletContext中调用一个名为getVirtualServerName()的方法,但是该方法不存在。这可能是由于以下原因之一导致的:

  1. Servlet API版本不兼容 - getVirtualServerName()方法是在Servlet API 3.1中引入的。如果代码是在旧版本的Servlet API下编译的,那么它将无法识别getVirtualServerName()方法。
  2. 应用程序服务器问题 - 如果正在运行应用程序服务器,并且该服务器未按照规范实现ServletContext接口,则可能会遇到此问题。在这种情况下,更新应用程序服务器可能解决问题。

修复问题

  1. 检查代码是否使用了正确版本的Servlet API。如果没有,请升级的Servlet API并重新编译代码。
  2. 确保正在运行符合规范的应用程序服务器。如果已经在使用符合规范的应用程序服务器,请尝试更新它以获取最新的Servlet API实现。
代码语言:javascript
复制
package com.wxapp.app;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
@MapperScan("com.wxapp.app.dao")
@MapperScan("com.wxapp.app.mapper")
public class AdminApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(AdminApplication.class);
        application.setBannerMode(Banner.Mode.OFF);
        application.run(args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        // 注意这里要指向原先用main方法执行的Application启动类
        return builder.sources(AdminApplication.class);
    }

}
代码语言:javascript
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>com.github.qcloudsms</groupId>
    <artifactId>qcloudsms</artifactId>
    <version>1.0.6</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>
<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.7</version>
</dependency>

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <!--            <version>3.4.3</version>-->
    <version>3.1.0</version>
</dependency>
<!-- redis依赖commons-pool 这个依赖一定要添加 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

<!--调用 HTTP 请求-->
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>3.8.1</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.6.5</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.6</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.72</version>
</dependency>
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.2.0</version>
</dependency>
代码语言:javascript
复制
@RestController
@RequestMapping("/article")
@Slf4j
public class ArticleController {
    @Autowired
    private ArticleService articleService;

    /**
     * 查询文章列表
     */
    @NoAuth
    @RequiresPermissions("article:list")
    @GetMapping("/listArticle")
    public JSONObject listArticle(HttpServletRequest request) {
        return articleService.listArticle(CommonUtil.request2Json(request));
    }
    /**
     * 新增文章
     */
    @NoAuth
    @RequiresPermissions("article:add")
    @PostMapping("/addArticle")
    public JSONObject addArticle(@RequestBody JSONObject requestJson) {
        CommonUtil.hasAllRequired(requestJson, "content");
        return articleService.addArticle(requestJson);
    }
    /**
     * 修改文章
     */
    @RequiresPermissions("article:update")
    @PostMapping("/updateArticle")
    public JSONObject updateArticle(@RequestBody JSONObject requestJson) {
        CommonUtil.hasAllRequired(requestJson, "id,content");
        return articleService.updateArticle(requestJson);
    }
}
代码语言:javascript
复制
public interface ArticleDao {
    /**
     * 新增文章
     */
    int addArticle(JSONObject jsonObject);
    /**
     * 统计文章总数
     */
    int countArticle(JSONObject jsonObject);
    /**
     * 文章列表
     */
    List<JSONObject> listArticle(JSONObject jsonObject);
    /**
     * 更新文章
     */
    int updateArticle(JSONObject jsonObject);
}
代码语言:javascript
复制
@Service
public class ArticleService {
    @Autowired
    private ArticleDao articleDao;
    /**
     * 新增文章
     */
    @Transactional(rollbackFor = Exception.class)
    public JSONObject addArticle(JSONObject jsonObject) {
        articleDao.addArticle(jsonObject);
        return CommonUtil.successJson();
    }
    /**
     * 文章列表
     */
    public JSONObject listArticle(JSONObject jsonObject) {
        CommonUtil.fillPageParam(jsonObject);
        int count = articleDao.countArticle(jsonObject);
        List<JSONObject> list = articleDao.listArticle(jsonObject);
        return CommonUtil.successPage(jsonObject, list, count);
    }
    /**
     * 更新文章
     */
    @Transactional(rollbackFor = Exception.class)
    public JSONObject updateArticle(JSONObject jsonObject) {
        articleDao.updateArticle(jsonObject);
        return CommonUtil.successJson();
    }
}
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.wxapp.app.dao.ArticleDao">
    <insert id="addArticle" parameterType="com.alibaba.fastjson.JSONObject">
        INSERT INTO article (content) VALUES (#{content})
    </insert>
    <select id="countArticle" resultType="Integer">
        SELECT COUNT(*) FROM article
    </select>
    <select id="listArticle" resultType="com.alibaba.fastjson.JSONObject">
        SELECT w.id,
               w.content,
               date_format(w.create_time, '%Y.%m.%d %T') createTime,
               date_format(w.update_time, '%Y.%m.%d %T') updateTime
        FROM article w
        ORDER BY w.id DESC
            LIMIT #{offSet}, #{pageRow}
    </select>
    <update id="updateArticle" parameterType="com.alibaba.fastjson.JSONObject">
        UPDATE article
        SET content = #{content}
        WHERE id = #{id}
    </update>
</mapper>
代码语言:javascript
复制
@Service
@Slf4j
public class LoginService {
    @Autowired
    private LoginDao loginDao;
    /**
     * 登录提交
     */
    public JSONObject authLogin(JSONObject jsonObject) {
        String phone = jsonObject.getString("phone");
        String code = jsonObject.getString("code");
        JSONObject info = new JSONObject();
        JSONObject user = loginDao.checkUser(phone, code);
        if (user == null) {
            throw null;
        }
        String successMsg = "登录成功";
        info.put("msg", successMsg);
        return CommonUtil.successJson(info);
    }

    /**
     * 查询当前登录用户的权限等信息
     */
    public JSONObject getInfo(JSONObject jsonObject) {
        //从session获取用户信息
        String phone = jsonObject.getString("phone");
        //SessionUserInfo userInfo = tokenService.getUserInfo();
        SessionUserInfo userInfo = loginDao.getUserInfo(phone);
        log.info(userInfo.toString());
        return CommonUtil.successJson(userInfo);
    }

    /**
     * 推出登录
     */
    public JSONObject logout() {
        JSONObject info = new JSONObject();
        String successMsg = "登出成功";
        info.put("msg", successMsg);
        return CommonUtil.successJson(info);
    }
}
代码语言:javascript
复制
public interface LoginDao {
    JSONObject checkUser(@Param("phone") String phone, @Param("code") String code);
    //Login login(@Param("phone") String phone, @Param("code") String code);

    SessionUserInfo getUserInfo(String phone);

    Set<String> getAllMenu();

    Set<String> getAllPermissionCode();
}
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.wxapp.app.dao.LoginDao">
    <select id="checkUser" resultType="com.alibaba.fastjson.JSONObject">
        SELECT * FROM la_user WHERE phone=#{phone} AND code=#{code}
    </select>

    <resultMap id="userInfo" type="com.wxapp.app.dto.session.SessionUserInfo">
        <id column="id" property="id"/>
        <result column="phone" property="phone"/>
        <result column="username" property="username"/>
        <result column="nickname" property="nickname"/>
        <collection property="roleIds" ofType="Integer">
            <id column="roleId"/>
        </collection>
        <collection property="menuList" ofType="String">
            <id column="menuCode"/>
        </collection>
        <collection property="permissionList" ofType="String">
            <id column="permissionCode"/>
        </collection>
    </resultMap>

    <select id="getUserInfo" resultMap="userInfo">
        SELECT u.id              id,
               u.phone,
               u.username,
               u.nickname,
               ur.role_id        roleId,
               p.menu_code       menuCode,
               p.permission_code permissionCode
        FROM la_user u
              LEFT JOIN sys_user_role ur on u.id = ur.user_id
              LEFT JOIN sys_role r ON r.id = ur.role_id
              LEFT JOIN sys_role_permission rp ON r.id = rp.role_id
              LEFT JOIN sys_permission p ON rp.permission_id = p.id
        WHERE u.phone=#{phone}
    </select>
    <select id="getAllMenu" resultType="String">
        select distinct(menu_code)
        from sys_permission;
    </select>
    <select id="getAllPermissionCode" resultType="String">
        select distinct(permission_code)
        from sys_permission;
    </select>
</mapper>
代码语言:javascript
复制
@RestController
@RequestMapping(value = "/login1")
@Slf4j
public class LoginController {

    @Autowired
    private LoginService loginService;

    /**
     * 登录
     */
    @NoAuth
    @RequestMapping(value = "/auth", method = RequestMethod.POST)
    //@PostMapping("/auth")
    public JSONObject authLogin(@RequestBody JSONObject requestJSON) {
        CommonUtil.hasAllRequired(requestJSON, "phone,code");
        return loginService.authLogin(requestJSON);
    }

    /**
     * 查询当前登录用户的信息
     */
    @NoAuth
    @PostMapping("/getInfo")
    public JSONObject getInfo(@RequestBody JSONObject requestJSON) {
        return loginService.getInfo(requestJSON);
    }

    /**
     * 登出
     */
    @PostMapping("/logout")
    public JSONObject logout() {
        return loginService.logout();
    }
}
代码语言:javascript
复制
package com.wxapp.app.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wxapp.app.pojo.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}
代码语言:javascript
复制
package com.wxapp.app.dto.session;

import lombok.Data;

import java.util.List;
import java.util.Set;

/**
 * 保存在session中的用户信息
 */
@Data
public class SessionUserInfo {
    private int id;
    private String phone;
    private String username;
    private String nickname;
    private List<Integer> roleIds;
    private Set<String> menuList;
    private Set<String> permissionList;
}
代码语言:javascript
复制
package com.wxapp.app.config.annotation;

public enum Logical {
    AND, OR, NOT;
}
代码语言:javascript
复制
package com.wxapp.app.config.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 访问此接口需要的权限
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermissions {
    String[] value();
    Logical logical() default Logical.AND;
}
代码语言:javascript
复制
package com.wxapp.app.common;

import lombok.Data;

@Data
public class Result<T> {

    //操作代码
    Integer code;

    //提示信息
    String message;

    //结果数据
    T data;

    public Result() {
    }

    public Result(ResultCode resultCode) {
        this.code = resultCode.code();
        this.message = resultCode.message();
    }

    public Result(ResultCode resultCode, T data) {
        this.code = resultCode.code();
        this.message = resultCode.message();
        this.data = data;
    }

    public Result(String message) {
        this.message = message;
    }

    public static Result SUCCESS() {
        return new Result(ResultCode.SUCCESS);
    }

    public static <T> Result SUCCESS(T data) {
        return new Result(ResultCode.SUCCESS, data);
    }

    public static Result FAIL() {
        return new Result(ResultCode.FAIL);
    }

    public static Result FAIL(String message) {
        return new Result(message);
    }

}

主要是Error creating bean with name 'sqlSessionFactory',这个是在XXX.xml(spring和mybatis整合的xml文件)中定义

并且出现的,其主要意思是xml文件没有配置好。原因大概有以下:

1.spring和mybatis结合的时候在spring配置bean的配置文件中没有把配置sqlSessionFactory中的dataSource和配置数据库连 连接池的dataSource放在同一个配置文件中;

2.mybatis的xml配置文件没有把mybatis的映射文件和Dao接口相互关联配置在一起;

3.虽然定义了数据库连接池的信息,但是可能没有连接到数据库;

我的解决方案:首先确认了数据库在数据库连接池中的配置并确定了数据库的连接状态完好,其次将有关dataSource有关的mybatis配置以及数据库连接池的配置都放在了同一个spring的xml配置文件中,最后再次需要确定spring、mybatis以及数据库连接池之间的相互关联关系是正常的,需要检查mybatis的xml配置文件和xml映射配置文件中相互映射(xml配置文件中的mapper以及xml映射文件中的namespace)是否正确。

代码语言:javascript
复制
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: org/mybatis/logging/LoggerFactory

解决方法:

在SpringBootApplication启动类上添加如下注解:

代码语言:javascript
复制
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
代码语言:javascript
复制
datasource:
  druid:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/ssm_db?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    username: root
    password:
ef0146f981c541266da767c195486f9e.png
ef0146f981c541266da767c195486f9e.png

image.png

service类上面没有@service注解或者mapper上没有@Repository注解,但是这种情况比较少见,一般不会忘记。

4c34ee707152d7d669990985d0199719.png
4c34ee707152d7d669990985d0199719.png

image.png

d2aa5d614db8adaa8b6edcfa638bdd23.png
d2aa5d614db8adaa8b6edcfa638bdd23.png

image.png

代码语言:javascript
复制
package com.wxapp.app;

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class AdminApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(AdminApplication.class);
        application.setBannerMode(Banner.Mode.OFF);
        application.run(args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        // 注意这里要指向原先用main方法执行的Application启动类
        return builder.sources(AdminApplication.class);
    }

}
  1. 确认 SysLoginService 类被正确地声明和定义,且放置在了 Spring 扫描的包路径下。
  2. 如果 SysLoginService 是通过注解方式来声明的,那么要确保该类上已经添加了正确的注解,例如 @Service@Component
  3. 如果 SysLoginService 类依赖于其它 Bean,请确保这些 Bean 已经正确地被声明和定义,并且也位于 Spring 扫描的包路径下。
  4. 确认在 Spring 配置文件中添加了正确的组件扫描配置,以便 Spring 能够扫描到 SysLoginService 类和其它相关的 Bean。

如果以上步骤都已确认无误,但问题仍然存在,可以考虑使用 Spring 的调试工具来进行进一步的排查。

代码语言:javascript
复制
package com.wxapp.app;

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class WxAppApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(WxAppApplication.class);
        application.setBannerMode(Banner.Mode.OFF);
        application.run(args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        // 注意这里要指向原先用main方法执行的Application启动类
        return builder.sources(WxAppApplication.class);
    }

}
基于mybatis开发,新功能基于mybatis-plus开发,同时依赖如下两个jar包

mybatis-spring-boot-starter

mybatis-plus-boot-starter

移除mybatis-spring-boot-starter依赖

原配置如下:

代码语言:javascript
复制
mybatis:
    mapperLocations: classpath:mapper/**/*.xml

新配置:

代码语言:javascript
复制
mybatis-plus:
  mapperLocations: classpath:mapper/**/*.xml,classpath:/mybatis-plus/*.xml

主要就是在解决从mybatis切换到mybatis-puls后,使用了BaseMapper的用户mapper接口与XXXmapper.xml文件方法绑定的问题,为使用了BaseMapper的用户mapper接口在内存中生成xml映射文件

关于Springboot+Mybatis——Error creating bean with name 'sqlSessionFactoryBean--Mapper映射问题

1.mybatis中

代码语言:javascript
复制
<!-- Spring-Mybatis -->
<dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.2</version>
        <!--原Mybatis中需排除下面2个依赖-->
        <exclusions>
                <exclusion>
                        <groupId>org.mybatis</groupId>
                        <artifactId>mybatis</artifactId>
                </exclusion>
                <exclusion>
                        <groupId>org.mybatis</groupId>
                        <artifactId>mybatis-spring</artifactId>
                </exclusion>
        </exclusions>
</dependency>
  1. pagehelper 中
代码语言:javascript
复制
<!-- 分页插件 -->
<dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.2.3</version>
        <!--需排除下面包-->
        <exclusions>
                <exclusion>
                        <groupId>org.mybatis.spring.boot</groupId>
                        <artifactId>mybatis-spring-boot-starter</artifactId>
                </exclusion>
        </exclusions>
</dependency>

3.引入[Mybatis-plus]

代码语言:javascript
复制
<!--引入Mybatis-plus-->
<dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.2</version>
</dependency>

4.引入autoconfigure

代码语言:javascript
复制
<!--引入autoconfigure-->
<dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
        <version>2.1.4</version>
</dependency>
  1. 修改配置文件,将原 mybatis 改成 mybatis-plus
代码语言:javascript
复制
mybatis-plus:
  mapper-locations: classpath*:/mapper/*.xml,classpath*:/lemon/mapper/*.xml
  configuration:
    mapUnderscoreToCamelCase: true
代码语言:javascript
复制
mybatis-plus:
  mapper-locations: classpath*:/mapper/*.xml,classpath*:/mapper/**/*.xml
  configuration:
    mapUnderscoreToCamelCase: true
4222896dc93776d15002447fc66cf318.png
4222896dc93776d15002447fc66cf318.png

SpringBoot启动流程.png

仓库地址:https://github.com/webVueBlog/JavaGuideInterview

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-05-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基于mybatis开发,新功能基于mybatis-plus开发,同时依赖如下两个jar包
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档