单点登录,其实看起来不是很复杂,只是细节上的处理,单点区分有三种
- 同域SSO
- 同父域SSO
- 跨域的SSO
如何实现同域SSO?
个人理解:当用户登录访问demo1.lzmvlog.top
时,同时具有访问demo2.lzmvlog.top
的能力,即认证完成一次,可以访问所有系统。
实现方式:可以采用Cookie
实现,即用户在访问一个系统时,携带认证颁发的信息,系统响应是否具有访问资格,否则跳转认证,也可以采用Session
,即Session
共享,校验访问用户是否具有有效的信息,提供访问资格
代码实现
依赖
<!--spring-data-jpa--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
配置
server: port: 8090 spring: application: name: authority datasource: url: jdbc:mysql://127.0.0.1:3306/SSO?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver
当用户访问除登录界面时,都需要提前认证,认证完成之后需要跳转到之前访问的路径上,并提供访问别的系统的权限。
实现逻辑,当用户访问任何路径时,都需要通过拦截器的校验,确认拥有访问的权限,才能放行通过,不具有访问权限的,重定向到 登录界面,并保存原有访问的页面路径,验证成功的时候跳转到原有页面
控制器
@Controller public class IndexController { @Autowired UserRepository userRepository; /** * 将要跳转的路径 */ public String url; /** * 登录界面 * * @return */ @GetMapping("/index") public String index() { return "index"; } /** * 登录界面 * * @return */ @GetMapping("/") public String index1() { return "index"; } /** * 登录请求接口 * * @param username 账号 * @param password 密码 * @param response * @return */ @PostMapping("login") public String login(String username, String password, HttpServletResponse response) { // 用户登录 boolean exists = userRepository.exists(Example.of(new User() .setUsername(username) .setPassword(password))); if (exists) { Cookie cookie = new Cookie("username", username); response.addCookie(cookie); // 如果正常访问即跳转到正常页面 if (StringUtils.isEmpty(url)) { return "demo1"; } // 如果之前存在访问的页面,认证完成即跳转会原有的页面 return url; } return "index"; } /** * 跳转到 demo2 * * @return */ @GetMapping("demo2") public String demo2() { return "demo2"; } /** * 跳转到 demo1 * * @return */ @GetMappi=ng("demo1") public String demo1() { return "demo1"; } }
拦截器实现
@Component public class CookieHandlerInterceptor implements HandlerInterceptor { @Autowired UserRepository userRepository; @Autowired IndexController indexController; /** * 执行方法之前 * * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 获取当前请求得路径 如果不是正常得登录界面请求 登录成功之后需要跳转到原来请求得界面上 String servletPath = request.getServletPath(); // 对不需要拦截得路径进行放行 if ("/index".equals(servletPath) || "/".equals(servletPath) || "/login".equals(servletPath)) { return true; } if (!"/index".equals(servletPath) || !"/".equals(servletPath)) { indexController.url = servletPath; } Cookie[] cookies = request.getCookies(); boolean exists = false; if (cookies != null) { for (Cookie cookie : cookies) { String value = cookie.getValue(); if (!StringUtils.isEmpty(value)) { exists = userRepository.exists(Example.of(new User() .setUsername(value))); } } } if (exists) { return true; } else { response.sendRedirect("/index"); } return false; } }
在SpringBoot2.x
之后不能生效,需要将拦截器添加到拦截器链路中,即:
@Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { /** * Session 拦截处理器 */ @Autowired private CookieHandlerInterceptor cookieHandlerInterceptor; /** * 添加拦截器 * * @param registry */ @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(this.cookieHandlerInterceptor).addPathPatterns("/**"); super.addInterceptors(registry); } }
其实拦截器还有第二种实现方式,即通过Filter
接口实现
@Component public class CookieFilter extends OncePerRequestFilter { @Autowired UserRepository userRepository; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 获取当前请求得路径 如果不是正常得登录界面请求 登录成功之后需要跳转到原来请求得界面上 String servletPath = request.getServletPath(); IndexController indexController = new IndexController(); // 对不需要拦截得路径进行放行 if ("/index".equals(servletPath) || "/".equals(servletPath) || "/login".equals(servletPath)) { filterChain.doFilter(request, response); } if (!"/index".equals(servletPath) || !"/".equals(servletPath)) { indexController.url = servletPath; } Cookie[] cookies = request.getCookies(); boolean exists = false; if (cookies != null) { for (Cookie cookie : cookies) { String value = cookie.getValue(); if (!StringUtils.isEmpty(value)) { exists = userRepository.exists(Example.of(new User() .setUsername(value))); } } } if (exists) { filterChain.doFilter(request, response); } else { response.sendRedirect("/"); } } }
其实也可以采用Session
的方式实现,采用共享Session
的方式,我这里只是简单的实现一下,其实在认证时可以结合SpringSecurity
或者Shiro
安全框架去整合JWT
以保证信息的安全
界面
index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <div align="center"> <h1>请登录</h1> <form action="/login" method="post"> <span>账号:</span><input name="username" type="text" value="zhang"><br> <span>密码:</span><input name="password" type="password" value="123456"><br> <button type="submit" style="margin: 10px 0">登录</button> </form> </div> </body> </html>
demo1.html
和demo2.html
只需要坐一下简单的区分,知道是哪个页面就行了
同域SSO
其实不是很复杂,只是了解一下整个访问的过程,和需要做的一些限制即可,后续看看做后面两种的实现
即同父域SSO
和跨域SSO
以上就是SpringBoot如何实现同域SSO(单点登录)的详细内容,更多关于SpringBoot 实现同域SSO的资料请关注自学编程网其它相关文章!
- 本文固定链接: https://zxbcw.cn/post/212109/
- 转载请注明:必须在正文中标注并保留原文链接
- QQ群: PHP高手阵营官方总群(344148542)
- QQ群: Yii2.0开发(304864863)