赞
踩
目前系统都是比较流行的微服务架构,在企业发展初期,企业使用的系统很少,通常一个或者两个,每个系统都有自己的登录模块,使用人员每天用自己的账号登录,很方便。但随着企业的发展,用到的微服务系统随之增多,使用人员在操作不同的系统时,需要多次登录,而且每个系统的账号都不一样,都要记录,这对于使用人员来说,很不方便还不友好。于是,就想到是不是可以在一个统一登录门户平台登录,其他系统就不用登录了呢?这就是单点登录要解决的问题。
单点登录英文全称Single sign On,简称就是SSO。它的解释是:在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。
使用当下最流行的SpringBoot2技术,持久层使用MyBatis,权限控制Spring Security,数据库MySql,基于OAuth2认证授权协议,构建一个易理解、高可用、高扩展性的分布式单点登录应用基层。
OAuth2为我们提供了四种授权方式:
授权码模式:授权码相对其他三种来说是功能比较完整、流程最安全严谨的授权方式,通过客户端的后台服务器与服务提供商的认证服务器交互来完成。
简化模式:这种模式不通过服务器端程序来完成,直接由浏览器发送请求获取令牌,令牌是完全暴露在浏览器中的,这种模式不太安全。
密码模式:密码模式也是比较常用到的一种,客户端向授权服务器提供用户名、密码然后得到授权令牌。这种模式不过有种弊端,我们的客户端需要存储用户输入的密码,但是对于用户来说信任度不高的平台是不可能让他们输入密码的。
客户端模式:客户端模式是客户端以自己的名义去授权服务器申请授权令牌,并不是完全意义上的授权。
上述简单的介绍了OAuth2内部的四种授权方式,我们下面使用授权码模式进行微服务单点登录实现,并且我们使用数据库存储用户登录信息、客户端授权信息。
创建Spring Boot2项目,版本:2.3.0,项目名称:cloud-sso-serve
- <!-- oauth2 -->
- <dependency>
- <groupId>org.springframework.security.oauth.boot</groupId>
- <artifactId>spring-security-oauth2-autoconfigure</artifactId>
- </dependency>
-
- <!-- thymeleaf 模板引擎 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-thymeleaf</artifactId>
- </dependency>
-
- <!-- spring security -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
- server.port=8087
- server.servlet.context-path=/sso
-
- #启用优雅关机
- server.shutdown=graceful
- #缓冲10秒
- spring.lifecycle.timeout-per-shutdown-phase=10s
-
- # mysql连接
- spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
- spring.datasource.url=jdbc:mysql://localhost:3306/sso?useUnicode=true&characterEncoding=UTF-8&userSSL=false&serverTimezone=GMT%2B8
- spring.datasource.username=root
- spring.datasource.password=root
- # druid web页面
- druid.login.enabled=false
- druid.login.username=druid
- druid.login.password=druid
- #druid连接池
- spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
- spring.datasource.initialSize=20
- spring.datasource.minIdle=30
- spring.datasource.maxActive=50
- spring.datasource.maxWait=60000
- spring.datasource.timeBetweenEvictionRunsMillis=60000
- spring.datasource.minEvictableIdleTimeMillis=300000
- spring.datasource.validationQuery=SELECT 'x'
- spring.datasource.testWhileIdle=true
- spring.datasource.testOnBorrow=true
- spring.datasource.testOnReturn=true
- spring.datasource.poolPreparedStatements=true
- spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
- spring.datasource.filters=stat
- spring.datasource.connectionProperties:druid.stat.slowSqlMillis=5000
-
- # MyBatis 配置
- mybatis.mapper-locations=classpath*:mapper/**/*Mapper.xml
- # mybatis-plus 配置
- mybatis-plus.mapper-locations=classpath*:mapper/**/*Mapper.xml
- mybatis.configuration.jdbc-type-for-null=null
-
- # thymeleaf 模板引擎配置
- spring.thymeleaf.mode=HTML5
- spring.thymeleaf.encoding=UTF-8
- spring.thymeleaf.servlet.content-type=text/html
- #开发时关闭缓存,不然没法看到实时页面
- spring.thymeleaf.cache=false
- #页面的存放路径就使用默认配置了
- spring.thymeleaf.prefix=classpath:/templates/
- spring.thymeleaf.check-template-location=true
- spring.thymeleaf.suffix=.html
- CREATE TABLE `oauth_client_details` (
- `client_id` VARCHAR(256) CHARACTER SET utf8 NOT NULL COMMENT '客户端唯一标识ID',
- `resource_ids` VARCHAR(256) CHARACTER SET utf8 DEFAULT NULL COMMENT '客户端所能访问的资源id集合',
- `client_secret` VARCHAR(256) CHARACTER SET utf8 DEFAULT NULL COMMENT '客户端访问密匙',
- `scope` VARCHAR(256) CHARACTER SET utf8 DEFAULT NULL COMMENT '客户端申请的权限范围',
- `authorized_grant_types` VARCHAR(256) CHARACTER SET utf8 DEFAULT NULL COMMENT '客户端授权类型',
- `web_server_redirect_uri` VARCHAR(256) CHARACTER SET utf8 DEFAULT NULL COMMENT '客户端的重定向URI',
- `authorities` VARCHAR(256) CHARACTER SET utf8 DEFAULT NULL COMMENT '客户端所拥有的权限值',
- `access_token_validity` INT(11) DEFAULT NULL COMMENT '客户端access_token的有效时间(单位:秒)',
- `refresh_token_validity` INT(11) DEFAULT NULL,
- `additional_information` VARCHAR(4096) CHARACTER SET utf8 DEFAULT NULL COMMENT '预留的字段',
- `autoapprove` VARCHAR(256) CHARACTER SET utf8 DEFAULT NULL COMMENT '是否跳过授权(true是,false否)',
- PRIMARY KEY (`client_id`)
- ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='客户端授权表'
-
- insert into `oauth_client_details`
- (`client_id`,`resource_ids`,`client_secret`,`scope`,`authorized_grant_types`,`web_server_redirect_uri`,`authorities`,`access_token_validity`,`refresh_token_validity`,`additional_information`,`autoapprove`) values
- ('client1',NULL,'$2a$10$zLD4yC3sL.n58Fh52EN3C.CKloW6GN3QeJrNPfGaqotaH04M2Ssm6','all','authorization_code,refresh_token','http://localhost:8086/client1/login',NULL,7200,NULL,NULL,'true'),
- ('client2',NULL,'$2a$10$zLD4yC3sL.n58Fh52EN3C.CKloW6GN3QeJrNPfGaqotaH04M2Ssm6','all','authorization_code,refresh_token','http://localhost:8085/client2/login',NULL,7200,NULL,NULL,'true');
-
- CREATE TABLE `sys_menu` (
- `menu_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '菜单ID',
- `menu_name` VARCHAR(50) NOT NULL COMMENT '菜单名称',
- `menu_vice_name` VARCHAR(50) NOT NULL COMMENT '菜单副名称',
- `parent_id` BIGINT(20) DEFAULT '0' COMMENT '父菜单ID',
- `order_num` INT(4) DEFAULT '0' COMMENT '显示顺序',
- `url` VARCHAR(200) DEFAULT '#' COMMENT '请求地址',
- `menu_type` CHAR(1) DEFAULT '' COMMENT '菜单类型(M目录 C菜单 F按钮)',
- `visible` CHAR(1) DEFAULT '0' COMMENT '菜单状态(0正常 1停用)',
- `perms` VARCHAR(100) DEFAULT NULL COMMENT '权限标识',
- `icon` VARCHAR(100) DEFAULT '#' COMMENT '菜单图标',
- `create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
- `create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
- `update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
- `update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
- `remark` VARCHAR(500) DEFAULT '' COMMENT '备注',
- PRIMARY KEY (`menu_id`)
- ) ENGINE=INNODB AUTO_INCREMENT=1091 DEFAULT CHARSET=utf8 COMMENT='菜单权限表'
-
- CREATE TABLE `sys_role` (
- `role_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
- `role_name` VARCHAR(30) NOT NULL COMMENT '角色名称',
- `role_key` VARCHAR(100) NOT NULL COMMENT '角色权限字符串',
- `role_sort` INT(4) NOT NULL COMMENT '显示顺序',
- `status` CHAR(1) NOT NULL COMMENT '角色状态(0正常 1停用 2删除)',
- `create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
- `create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
- `update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
- `update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
- `remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
- PRIMARY KEY (`role_id`)
- ) ENGINE=INNODB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8 COMMENT='角色信息表'
-
- CREATE TABLE `sys_role_menu` (
- `role_id` BIGINT(20) NOT NULL COMMENT '角色ID',
- `menu_id` BIGINT(20) NOT NULL COMMENT '菜单ID',
- PRIMARY KEY (`role_id`,`menu_id`)
- ) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='角色和菜单关联表'
-
- CREATE TABLE `sys_user` (
- `user_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
- `dept_id` BIGINT(20) DEFAULT NULL COMMENT '部门ID',
- `login_name` VARCHAR(30) NOT NULL COMMENT '登录名称',
- `user_name` VARCHAR(30) DEFAULT NULL COMMENT '用户名称',
- `email` VARCHAR(50) DEFAULT '' COMMENT '用户邮箱',
- `phone` VARCHAR(11) DEFAULT '' COMMENT '手机号码',
- `telephone` VARCHAR(12) DEFAULT '' COMMENT '座机号码',
- `duty` VARCHAR(30) DEFAULT '' COMMENT '职务',
- `sex` CHAR(1) DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',
- `avatar` VARCHAR(100) DEFAULT '' COMMENT '头像路径',
- `password` VARCHAR(100) DEFAULT '' COMMENT '密码',
- `salt` VARCHAR(20) DEFAULT '' COMMENT '盐加密',
- `status` CHAR(1) DEFAULT '0' COMMENT '状态(0正常 1停用 2删除)',
- `login_ip` VARCHAR(50) DEFAULT '' COMMENT '最后登陆IP',
- `login_date` DATETIME DEFAULT NULL COMMENT '最后登陆时间',
- `create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
- `create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
- `update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
- `update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
- `remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
- PRIMARY KEY (`user_id`)
- ) ENGINE=INNODB AUTO_INCREMENT=61 DEFAULT CHARSET=utf8 COMMENT='用户信息表'
-
- insert into `sys_user`
- (`user_id`,`dept_id`,`login_name`,`user_name`,`email`,`phone`,`telephone`,`duty`,`sex`,`avatar`,`password`,`salt`,`status`,`login_ip`,`login_date`,`create_by`,`create_time`,`update_by`,`update_time`,`remark`) values
- (1,1,'admin','admin','admin@sina.cn','','','','0','','123','111','0','10.96.217.201','2021-02-25 11:15:51','',NULL,'',NULL,NULL);
-
- CREATE TABLE `sys_user_role` (
- `user_id` BIGINT(20) NOT NULL COMMENT '用户ID',
- `role_id` BIGINT(20) NOT NULL COMMENT '角色ID',
- PRIMARY KEY (`user_id`,`role_id`)
- ) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='用户和角色关联表'
-
1.4.1 AuthorizationServerConfig客户端授权配置
- package com.modules.common.config;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
- import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
- import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
- import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
- import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
- import org.springframework.security.oauth2.provider.token.TokenStore;
- import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
- import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
-
- import javax.sql.DataSource;
-
- /**
- * 客户端授权配置
- */
- @Configuration
- @EnableAuthorizationServer // 开启授权服务器
- public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
-
- @Autowired
- private DataSource dataSource;
-
- /**
- * 配置第三方应用,可以放在内存(inMemory),数据库
- * 四种授权模式("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
- * 1、授权码模式(authorization code)(正宗方式)(支持refresh token)
- * 2、密码模式(password)(为遗留系统设计)(支持refresh token)
- * 3、客户端模式(client_credentials)(为后台api服务消费者设计)(不支持refresh token)
- * 4、简化模式(implicit)(为web浏览器应用设计)(不支持refresh token)
- *
- * @param clients
- * @throws Exception
- */
- @Override
- public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
- clients.jdbc(dataSource);
- }
-
- /**
- * 需要暴露授权服务器端点
- *
- * @param endpoints
- * @throws Exception
- */
- @Override
- public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
- endpoints.accessTokenConverter(jwtAccessTokenConverter());
- endpoints.tokenStore(jwtTokenStore());
- // endpoints.tokenServices(defaultTokenServices());
- }
-
- @Override
- public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
- security.allowFormAuthenticationForClients();
- security.tokenKeyAccess("isAuthenticated()");
- }
-
-
- /**
- * 配置TokenStore,有多种实现方式,redis,jwt,jdbc
- * @return
- */
- @Bean
- public TokenStore jwtTokenStore() {
- return new JwtTokenStore(jwtAccessTokenConverter());
- }
-
- @Bean
- public JwtAccessTokenConverter jwtAccessTokenConverter(){
- JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
- converter.setSigningKey("testKey");
- return converter;
- }
- }
1.4.2 SpringSecurityConfig权限配置
- package com.modules.common.config;
-
- import com.modules.system.service.SysLoginService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
- import org.springframework.security.config.annotation.web.builders.HttpSecurity;
- import org.springframework.security.config.annotation.web.builders.WebSecurity;
- import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
- import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
- import org.springframework.security.core.userdetails.UserDetailsService;
- import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
- import org.springframework.security.crypto.password.PasswordEncoder;
-
- /**
- * spring security配置
- * @author li'chao
- */
- @Configuration
- @EnableWebSecurity
- public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
-
-
- /**
- * 自定义用户认证逻辑
- */
- @Autowired
- private SysLoginService sysLoginService;
-
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.formLogin()
- .loginPage("/login")
- .and()
- .authorizeRequests()
- .antMatchers("/login").permitAll()
- .anyRequest()
- .authenticated()
- .and().csrf().disable().cors();
- /* http
- .requestMatchers().antMatchers("/oauth/**","/login/**","/logout/**")
- .and()
- .authorizeRequests()
- .antMatchers("/oauth/**").authenticated()
- .and()
- .formLogin().permitAll();*/
- }
-
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(sysLoginService).passwordEncoder(passwordEncoder());
- }
-
- /**
- * 强散列哈希加密实现
- */
- @Bean
- public PasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
-
- @Override
- public void configure(WebSecurity web) throws Exception {
- web.ignoring().antMatchers("/assets/**", "/css/**", "/images/**");
- }
-
- }
-
1.4.3 SysLoginService登录处理业务
- package com.modules.system.service;
-
- import com.baomidou.mybatisplus.toolkit.CollectionUtils;
- import com.modules.system.entity.SysUser;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.security.core.authority.SimpleGrantedAuthority;
- import org.springframework.security.core.userdetails.User;
- import org.springframework.security.core.userdetails.UserDetails;
- import org.springframework.security.core.userdetails.UserDetailsService;
- import org.springframework.security.core.userdetails.UsernameNotFoundException;
- import org.springframework.security.crypto.password.PasswordEncoder;
- import org.springframework.stereotype.Component;
-
- import java.util.ArrayList;
- import java.util.List;
-
- /**
- * 用户登录 业务层
- * @author li'chao
- *
- */
- @Slf4j
- @Component
- public class SysLoginService implements UserDetailsService {
-
- @Autowired
- private PasswordEncoder passwordEncoder;
-
- @Autowired
- private SysUserService sysUserService;
-
- @Autowired
- private SysMenuService sysMenuService;
-
- @Override
- public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
- // 根据登录名称查询用户信息
- SysUser sysUser = sysUserService.selectUserByLoginName(name);
- if (null == sysUser) {
- log.warn("用户{}不存在", name);
- throw new UsernameNotFoundException(name);
- }
- // 根据用户ID查询权限配置的菜单,获取菜单标识字段perms
- List<String> permsList = sysMenuService.selectPermsListByUserId(sysUser.getUserId());
- permsList.remove(null);
- List<SimpleGrantedAuthority> authorityList = new ArrayList<>();
- if(!CollectionUtils.isEmpty(permsList)){
- for(String str : permsList){
- authorityList.add(new SimpleGrantedAuthority(str));
- }
- }
-
- return new User(sysUser.getLoginName(), passwordEncoder.encode(sysUser.getPassword()), authorityList);
- }
- }
1.4.4 LoginController登录处理
- package com.modules.system.controller;
-
- import com.modules.common.web.BaseController;
- import com.modules.system.service.SysUserService;
- import io.swagger.annotations.Api;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.CrossOrigin;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
-
- /**
- * 登录管理
- * @author lc
- */
- @Api(tags = "登录管理")
- @Slf4j
- @CrossOrigin
- @Controller
- public class LoginController extends BaseController
- {
-
- @Autowired
- private SysUserService userService;
-
- /**
- * 自定义登录页面
- * @return
- */
- @GetMapping("/login")
- public String login() {
- return "login";
- }
-
- /**
- * 登录成功后显示的首页
- * @return
- */
- @GetMapping("/")
- public String index() {
- return "index";
- }
-
- @RequestMapping("oauth/exit")
- public void exit(HttpServletRequest request, HttpServletResponse response) {
- new SecurityContextLogoutHandler().logout(request, null, null);
- try {
- System.out.println(request.getHeader("referer"));
- response.sendRedirect(request.getHeader("referer"));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
1.4.5 自定义登录页面
- <!DOCTYPE html>
- <html xmlns:th="http://www.thymeleaf.org">
- <head>
- <meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <title>Ela Admin - HTML5 Admin Template</title>
- <meta name="description" content="Ela Admin - HTML5 Admin Template">
- <meta name="viewport" content="width=device-width, initial-scale=1">
-
- <link type="text/css" rel="stylesheet" th:href="@{/assets/css/normalize.css}">
- <link type="text/css" rel="stylesheet" th:href="@{/assets/bootstrap-4.3.1-dist/css/bootstrap.min.css}">
- <link type="text/css" rel="stylesheet" th:href="@{/assets/css/font-awesome.min.css}">
- <link type="text/css" rel="stylesheet" th:href="@{/assets/css/style.css}">
-
- </head>
- <body class="bg-dark">
- <script type="text/javascript" th:src="@{/assets/js/jquery-2.1.4.min.js}"></script>
- <script type="text/javascript" th:src="@{/assets/bootstrap-4.3.1-dist/js/bootstrap.min.js}"></script>
- <script type="text/javascript" th:src="@{/assets/js/main.js}"></script>
- <div class="sufee-login d-flex align-content-center flex-wrap">
- <div class="container">
- <div class="login-content">
- <div class="login-logo">
- <h1 style="color: #57bf95;">统一登录综合管理平台</h1>
- </div>
- <div class="login-form">
- <form th:action="@{/login}" method="post">
- <div class="form-group">
- <label>登录名称</label>
- <input type="text" class="form-control" name="username" placeholder="请输入登录名称">
- </div>
- <div class="form-group">
- <label>密码</label>
- <input type="password" class="form-control" name="password" placeholder="请输入登录密码">
- <span style="color: red" ></span>
- </div>
- <div class="checkbox">
- <label>
- <input type="checkbox"> 记住我
- </label>
- <label class="pull-right">
- <a href="#">忘记密码</a>
- </label>
- </div>
- <button type="submit" class="btn btn-success btn-flat m-b-30 m-t-30" style="font-size: 18px;">登录</button>
- <p style="color: red" th:if="${param.error}" th:text="${session?.SPRING_SECURITY_LAST_EXCEPTION?.message}" ></p>
- </form>
- </div>
- </div>
- </div>
- </div>
- </body>
- </html>
1.4.6 登录成功后首页
- <!DOCTYPE html>
- <html lang="en" >
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
- <title>综合管理平台</title>
- <link rel="stylesheet" href="css/style.css">
- </head>
- <body>
- <div class="container-scroller">
- <!-- partial -->
- <div class="container-fluid page-body-wrapper">
- <div class="main-panel">
- <div class="content-wrapper">
- <div class="page-header">
- <h3 class="page-title">
- <span class="page-title-icon bg-gradient-primary text-white mr-2">
- <i class="mdi mdi-home"></i>
- </span>
- 欢迎来到综合管理平台
- </h3>
- </div>
- <div class="row">
- <div class="col-md-4 stretch-card grid-margin">
- <div class="card bg-gradient-danger card-img-holder text-white">
- <div class="card-body">
- <img src="images/dashboard/circle.svg" class="card-img-absolute" alt="circle-image"/>
- <h2 class="mb-5"><a href="http://localhost:8086/client1/list" style="color: white">商品管理系统</a></h2>
- </div>
- </div>
- </div>
- <div class="col-md-4 stretch-card grid-margin">
- <div class="card bg-gradient-info card-img-holder text-white">
- <div class="card-body">
- <img src="images/dashboard/circle.svg" class="card-img-absolute" alt="circle-image"/>
- <h2 class="mb-5"><a href="http://localhost:8083/orderSystem/order/list" style="color: white">订单管理系统</a></h2>
- </div>
- </div>
- </div>
- <div class="col-md-4 stretch-card grid-margin">
- <div class="card bg-gradient-success card-img-holder text-white">
- <div class="card-body">
- <img src="images/dashboard/circle.svg" class="card-img-absolute" alt="circle-image"/>
- <h2 class="mb-5">营销管理系统</h2>
- </div>
- </div>
- </div>
- </div>
-
- <div class="row">
- <div class="col-md-4 stretch-card grid-margin">
- <div class="card bg-gradient-danger card-img-holder text-white">
- <div class="card-body">
- <img src="images/dashboard/circle.svg" class="card-img-absolute" alt="circle-image"/>
- <h2 class="mb-5">运营管理系统</h2>
- </div>
- </div>
- </div>
- <div class="col-md-4 stretch-card grid-margin">
- <div class="card bg-gradient-info card-img-holder text-white">
- <div class="card-body">
- <img src="images/dashboard/circle.svg" class="card-img-absolute" alt="circle-image"/>
- <h2 class="mb-5">商户管理系统</h2>
- </div>
- </div>
- </div>
- <div class="col-md-4 stretch-card grid-margin">
- <div class="card bg-gradient-success card-img-holder text-white">
- <div class="card-body">
- <img src="images/dashboard/circle.svg" class="card-img-absolute" alt="circle-image"/>
- <h2 class="mb-5">财务管理系统</h2>
- </div>
- </div>
- </div>
- </div>
- </div>
- <!-- content-wrapper ends -->
- <!-- partial -->
- </div>
- <!-- main-panel ends -->
- </div>
- <!-- page-body-wrapper ends -->
- </div>
- <!-- container-scroller -->
- </body>
- </html>
1.4.7 测试
登录页面:admin/123
登录成功
至此,授权服务实现完成,此间使用授权码模式实现,客户端信息存入数据库,登录页面进行自定义。
创建Spring Boot2项目,版本:2.3.0,项目名称:cloud-goods-client
- <!-- oauth2 -->
- <dependency>
- <groupId>org.springframework.security.oauth.boot</groupId>
- <artifactId>spring-security-oauth2-autoconfigure</artifactId>
- </dependency>
-
- <!-- thymeleaf 模板引擎 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-thymeleaf</artifactId>
- </dependency>
-
- <!-- spring security -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
- server:
- port: 8086
- servlet:
- context-path: /client1
-
- auth-server: http://localhost:8087/sso
-
- security:
- oauth2:
- client:
- client-id: client1
- client-secret: 123456
- user-authorization-uri: ${auth-server}/oauth/authorize
- access-token-uri: ${auth-server}/oauth/token
- resource:
- jwt:
- key-uri: ${auth-server}/oauth/token_key
-
- # thymeleaf 模板引擎配置
- spring:
- thymeleaf:
- mode: HTML5
- encoding: utf-8
- servlet:
- content-type: text/html
- # 开发时关闭缓存,不然没法看到实时页面
- cache: true
- # 页面的存放路径就使用默认配置了
- prefix: classpath:/templates/
- check-template-location: true
- suffix: .html
2.3.1 WebSecurityConfigurer配置
- package com.modules.config;
-
-
- import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
- import org.springframework.security.config.annotation.web.builders.HttpSecurity;
- import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
- import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-
- @Configuration
- @EnableWebSecurity
- @EnableGlobalMethodSecurity(prePostEnabled = true)
- @EnableOAuth2Sso
- public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
-
- @Override
- public void configure(HttpSecurity http) throws Exception {
- http.antMatcher("/**").authorizeRequests()
- .anyRequest().authenticated();
- }
- }
2.3.2 TestController 测试
- package com.modules.controller;
-
- import org.springframework.security.access.prepost.PreAuthorize;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.GetMapping;
-
- @Controller
- public class TestController {
-
- @GetMapping("/list")
- @PreAuthorize("hasAuthority('ROLE_NORMAL')")
- public String list( ) {
- return "list";
- }
- }
2.3.3 自定义模拟列表显示
- <!DOCTYPE html>
- <html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
- <head>
- <meta charset="UTF-8">
- <title>商品管理系统</title>
- </head>
- <body>
- <h3><span>商品管理系统</span> | <a th:href="@{/logout}">退出</a> </h3>
- <h2>这是一个商品管理列表页面</h2>
- </body>
- </html>
2.3.4 测试
访问客户端 http://localhost:8086/client1/list 自动跳转授权服务器登录页面 http://localhost:8087/upms/login
输入登录名称和密码,自动跳转到客户端 http://localhost:8086/client1/list
访问服务器 http://localhost:8087/upms/
因为我在商品管理系统添加了超链接 http://localhost:8086/client1/list,点击商品管理系统,自动跳转到客户端
至此,客户端微服务集成单点登录完成,多个客户端服务如此集成即可。
源码地址:https://gitee.com/lichao12/spring-boot2-security-oauth2-jwt
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。