文章目录
创建聚合工程
项目架构
工程结构
创建工程
第一步:创建parent工程,例如:
第二步:创建jt-sso-auth工程
第三步:创建jt-sso-resource工程
第四步:创建jt-sso-gateway工程
第五步:创建jt-sso-ui工程
配置项目工程
jt-cloud-sso
打开工程pom文件,添加依赖配置,代码如下:
<dependencyManagement>
<!--Spring Framework与SpringBoot的关系
Spring框架是资源整合框架,基于IOC思想进行资源整合.SpringBoot基于Spring框架,
用于简化Spring框架整合资源的工程,同时为微服务工程创建和配置提供了更加便利条件
-->
<dependencies>
<!--Spring Boot依赖(spring-boot-starter-parent)
思考:05-jt-sca工程中依赖的添加方式-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.2.RELEASE</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<!--Spring Cloud 依赖(定义了微服务规范)-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<!--Spring Cloud Alibaba依赖(基于spring微服务规范做了具体落地实现)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
jt-sso-common
打开工程pom文件,添加依赖配置,代码如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--provided 表示这个依赖只在编译阶段有效-->
<scope>provided</scope>
</dependency>
</dependencies>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
jt-sso-auth
pom.xml
<dependencies> <!--spring web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--spring security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- jwt --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <!--spring cloud alibaba nacos discovery --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--spring cloud alibaba nacos config --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!--jt-common--> <dependency> <groupId>com.jt</groupId> <artifactId>jt-sso-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependencies</span><span class="token punctuation">></span></span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
bootstrap.yml
在resource目录下创建bootstrap.yml配置文件,代码如下:
server:
port: 8081
spring:
application:
name: jt-sso-auth
datasource:
url: jdbc:mysql:///jt_security?serverTimezone=Asia/Shanghai&characterEncoding=utf8
username: root
password: root
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yml
logging:
level:
com.jt: debug
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
启动类
package com.jt.sso;
@SpringBootApplication
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApp.class,args);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
jt-sso-resource
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.jt</groupId>
<artifactId>jt-sso-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
bootstrap.yml
server:
port: 8090
spring:
application:
name: jt-sso-resource
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yml
logging:
level:
com.jt: debug
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
启动类
package com.jt;
@EnableGlobalMethodSecurity(prePostEnabled = true)
@SpringBootApplication
public class ResourceApplication {
public static void main(String[] args) {
SpringApplication.run(ResourceApplication.class,args);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
jt-sso-gateway
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
bootstrap.yml
server:
port: 9000
spring:
application:
name: jt-sso-gateway
cloud:
nacos:
discovery: #服务发现
server-addr: localhost:8848
config: #服务配置
server-addr: localhost:8848
file-extension: yml
gateway:
discovery:
locator:
enabled: true #开启基于服务名查找服务实例
routes:
- id: router01
uri: lb://jt-sso-auth #jt-sso-auth 服务名
predicates:
- Path=/auth/login
filters:
- StripPrefix=1 #去掉请求path中的第一层目录
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
启动类
package com.jt;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
jt-sso- ui
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
- 1
- 2
- 3
- 4
- 5
- 6
application.yml
server:
port: 80
- 1
- 2
启动类
package com.jt;
@SpringBootApplication
public class UIApplication {
public static void main(String[] args) {
SpringApplication.run(UIApplication.class,args);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
工程业务代码实现
jt-sso-common
代码结构
说明:途中代码可从前面写过的单点登陆系统去拷贝。
问题分析
这个工程中添加的web依赖,为什么添加scope元素的值为provided.
jt-sso-auth
代码结构
说明:途中代码可从前面写过的单点登陆系统去拷贝。
AuthController
创建AuthController对象用于对外提供令牌的解析任务,代码如下:
package com.jt.sso.controller;
@RestController
public class AuthController {
@GetMapping("/auth/info")
public Map<String,Object> getAuthentication(String token){
System.out.println("token==="+token);
Claims claims=JwtUtils.getClaimsFromToken(token);
boolean flag=claims.getExpiration().before(new Date());
String username=(String)claims.get("username");
List<String> list=(List<String>)
claims.get("authorities");
Map<String,Object> map=new HashMap<>();
map.put("expired",flag);
map.put("username",username);
map.put("authorities",list);
return map;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
定义完这个controller以后,需要修改SpringConfig类中的configure(HttpSecurity http)方法, 对解析令牌的/auth/info这个路径进行放行,例如
...
http.authorizeRequests()
.antMatchers("/auth/info")
.permitAll()
.anyRequest()//所有请求->对应任意资源
.authenticated();//必须认证
- 1
- 2
- 3
- 4
- 5
- 6
- 7
问题分析
jt-sso-resource
代码结构
RestTemplate服务调用
第一步:在启动类中,初始化一个RestTemplate对象,例如:
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
- 1
- 2
- 3
- 4
- 5
第二步:在TokenInterceptor中添加如下代码,初始化RestTemplate对象
private RestTemplate restTemplate;
public TokenInterceptor(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
- 1
- 2
- 3
- 4
第三步:修改preHandle方法,基于RestTemplate进行远程服务调用,代码如下:
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
//1.从请求中获取token对象(如何获取取决于你传递token的方式:header,params)
String token=request.getHeader("token");
//2.验证token是否存在
if(token==null||"".equals(token))
throw new RuntimeException("请先登录");//WebUtils.writeJsonToClient
//3.验证token是否过期,解析token中的认证和权限信息(一般存储在jwt格式中的负载部分)
//基于restTemplate进行远程服务调用
String url="http://jt-sso-auth/auth/info?token="+token;
Map<String,Object> map= restTemplate.getForObject(url,Map.class);
Boolean expired=(Boolean)map.get("expired");
if(expired)throw new RuntimeException("登录超时");
String username=(String)map.get("username");
List<String> list=(List<String>)map.get("authorities");
//4.封装和存储认证和权限信息
//4.1构建UserDetail对象(用户身份的象征-类似于一张名片,微信的二维码)
UserDetails userDetails=User.builder()
.username(username)
.password("")
.authorities(list.toArray(new String[]{}))
.build();
//4.2构建Security权限交互对象(记住,固定写法)
PreAuthenticatedAuthenticationToken authToken=
new PreAuthenticatedAuthenticationToken(
userDetails,//用户身份
userDetails.getPassword(),
userDetails.getAuthorities());
//4.3将权限交互对象与当前请求进行绑定
authToken.setDetails(new WebAuthenticationDetails(request));
//5.4.将认证后的token存储到Security上下文(会话对象)
SecurityContextHolder.getContext().setAuthentication(authToken);
return true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
第四步:在拦截器的配置类中,修改如下代码:
@Autowired
private RestTemplate restTemplate;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenInterceptor(restTemplate))
//配置要拦截的url
.addPathPatterns("/**");//doCreate,doUpdate,doDelete
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
第五步:启动工程基于postman进行访问测试
Feign方式服务调用
第一步:添加Feign依赖
org.springframework.cloud
spring-cloud-starter-openfeign
第二步:在启动类上添加启动Feign应用注解
@EnableFeignClients
- 1
第三步:定义feign远程调用接口
package com.jt.res.service;
@FeignClient(name = “jt-sso-auth”,contextId = “remoteAuthService”)
public interface RemoteAuthService {
//@GetMapping中的地址为jt-sso-auth服务中的一个地址
@GetMapping("/auth/info")
public Map<String,Object> getAuthentication(
@RequestParam(“token”) String token);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
第四步:在TokenInterceptor中定义RemoteAuthService属性及构造方法,代码如下:
private RemoteAuthService remoteAuthService;
public TokenInterceptor(RemoteAuthService remoteAuthService) {
this.remoteAuthService = remoteAuthService;
}
- 1
- 2
- 3
- 4
- 5
- 6
第五步:修改拦截器的preHandle方法,基于Feign进行远程服务调用,代码如下:
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //1.从请求中获取token对象(如何获取取决于你传递token的方式:header,params) String token=request.getHeader("token"); //2.验证token是否存在 if(token==null||"".equals(token)) throw new RuntimeException("请先登录");//WebUtils.writeJsonToClient //3.验证token是否过期,解析token中的认证和权限信息(一般存储在jwt格式中的负载部分) //去认证中心获取这些数据?(作业-RestTemplate或者Feign) //基于feign方式进行远程服务调用 Map<String,Object> map=remoteAuthService.getAuthentication(token); Boolean expired=(Boolean)map.get("expired"); if(expired)throw new RuntimeException("登录超时"); String username=(String)map.get("username"); List<String> list=(List<String>)map.get("authorities");
<span class="token comment">//4.封装和存储认证和权限信息</span> <span class="token comment">//4.1构建UserDetail对象(用户身份的象征-类似于一张名片,微信的二维码)</span> <span class="token class-name">UserDetails</span> userDetails<span class="token operator">=</span><span class="token class-name">User</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">username</span><span class="token punctuation">(</span>username<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">password</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">authorities</span><span class="token punctuation">(</span>list<span class="token punctuation">.</span><span class="token function">toArray</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//4.2构建Security权限交互对象(记住,固定写法)</span> <span class="token class-name">PreAuthenticatedAuthenticationToken</span> authToken<span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">PreAuthenticatedAuthenticationToken</span><span class="token punctuation">(</span> userDetails<span class="token punctuation">,</span><span class="token comment">//用户身份</span> userDetails<span class="token punctuation">.</span><span class="token function">getPassword</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> userDetails<span class="token punctuation">.</span><span class="token function">getAuthorities</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//4.3将权限交互对象与当前请求进行绑定</span> authToken<span class="token punctuation">.</span><span class="token function">setDetails</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">WebAuthenticationDetails</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//5.4.将认证后的token存储到Security上下文(会话对象)</span> <span class="token class-name">SecurityContextHolder</span><span class="token punctuation">.</span><span class="token function">getContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setAuthentication</span><span class="token punctuation">(</span>authToken<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
第六步:在拦截器的配置类中,修改如下代码:
@Autowired
private RemoteAuthService remoteAuthService;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenInterceptor(remoteAuthService))
//配置要拦截的url
.addPathPatterns("/**");//doCreate,doUpdate,doDelete
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
第七步:启动工程基于postman进行访问测试
问题分析
- 依赖注入问题(spring只为它管理的对象指定属性赋值)
- Feign方式和RestTemplate 方式服务调用过程。
jt-sso-gateway
跨域设计
当客户端基于ajax访问服务端资源时,因为跨域问题不能直接访问,我们可以在服务端通过过滤器,打开跨域访问。例如:
package com.jt.gateway.config;
@Configuration
public class CorsFilterConfig {
@Bean
public CorsWebFilter corsFilter(){
System.out.println(“corsWebFilter()”);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
CorsConfiguration corsConfiguration = new CorsConfiguration();
//1.配置跨域
//允许哪种请求头跨域
corsConfiguration.addAllowedHeader("");
//允许哪种方法类型跨域 get post delete put
corsConfiguration.addAllowedMethod("");
// 允许哪些请求源跨域
corsConfiguration.addAllowedOrigin("*");
// 是否携带cookie跨域
corsConfiguration.setAllowCredentials(true);
//允许跨域的路径
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
第六步:在拦截器的配置类中,修改如下代码:
jt-sso- ui
代码结构
将以前做单点登录系统时,ui工程中的static目录拷贝到当前项目,然后修改login.html,index.html中访问网关的url,最后对static目录进行rebuild,然后启动项目进行访问测试.