用户认证 Zuul
流程:
搭建环境
用户中心负责用户管理,包括:用户信息管理、角色管理、权限管理等
1、创建xc_user数据库(MySQL)
2、创建用户中心工程:xc-service-ucenter
3、查询用户接口
完成用户中心根据账号查询用户信息接口功能
API
在UcenterControllerApi接口中定义getUserext方法
DAO
在XcUserRepository接口中定义findXcUserByUsername方法
在XcCompanyUserRepository接口中定义findByUserId方法
Service
在UserService类中定义findXcUserByUsername、getUserExt方法
Controller
在UcenterController类中定义getUserext方法
测试:
打开浏览器输入:http://localhost:40300/swagger-ui.html
点击:
输入用户名:itcast,查询结果如下:
调用查询用户接口
1、创建client
认证服务需要远程调用用户中心服务查询用户,在认证服务xc-service-ucenter-auth中创建Feign客户端
在UserClient接口中定义getUserext方法
2、UserDetailsServiceImpl
认证服务调用spring security接口申请令牌,spring security接口会调用UserDetailsServiceImpl从数据库查询用 户,如果查询不到则返回 NULL,表示不存在;在UserDetailsServiceImpl中将正确的密码返回, spring security 会自动去比对输入密码的正确性
1、修改UserDetailsServiceImpl的loadUserByUsername方法,调用Ucenter服务的查询用户接口
2、测试,请求:http://localhost:40400/auth/userlogin
观察UserDetailsServiceImpl是否正常请求Ucenter的查询用户接口。
3、BCryptPasswordEncoder
早期使用md5对密码进行编码,每次算出的md5值都一样,这样非常不安全,Spring Security推荐使用 BCryptPasswordEncoder对密码加随机盐,每次的Hash值都不一样,安全性高
1、调用TestClient类中的testPasswrodEncoder测试方法
2、在AuthorizationServerConfig配置类中配置BCryptPasswordEncoder
3、测试请求:http://localhost:40400/auth/userlogin,输入正常的账号和密码进行测试
测试结果:
解析申请令牌错误信息
当账号输入错误应该返回用户不存在的信息,当密码错误要返回用户名或密码错误信息,业务流程图如下:
修改申请令牌的程序解析返回的错误:
由于restTemplate收到400或401的错误会抛出异常,而spring security针对账号不存在及密码错误会返回400及 401,所以在代码中控制针对400或401的响应不要抛出异常
用户不存在:
密码错误:
4、测试使用postman请求:http://localhost:40400/auth/userlogin
1、输入正确的账号和密码进行测试
从数据库找到测试账号,本课程所提供的用户信息初始密码统一为111111
2、输入错误的账号和密码进行测试
JWT查询接口
认证服务对外提供JWT查询接口,流程如下:
1、客户端携带cookie中的身份令牌请求认证服务获取JWT
2、认证服务根据身份令牌从redis中查询jwt令牌并返回给客户端
API
在AuthControllerApi接口中添加userjwt方法;
Service
在AuthService类中添加getUserToken方法;
Controller
在AuthController类中添加userjwt方法;
测试
使用postman测试
1、请求:http://ucenter.xuecheng.com/openapi/auth/userlogin
观察cookie是否已存入用户身份令牌。
2、get:http://ucenter.xuecheng.com/openapi/auth/userjwt
Nginx中配置代理转发
上边实现在首页显示当前用户信息,首页需要通过Nginx代理请求认证服务,所以需要在首页的虚拟主机上配置代 理路径:
#认证 location ^~ /openapi/auth/ { proxy_pass http://auth_server_pool/auth/; }
注意:其它前端系统要接入认证要请求认证服务也需要配置上边的代理路径。
用户退出
用户退出要有以下动作:
1、删除redis中的token
2、删除cookie中的token
1、API
添加logout方法;
2、Service
添加delToken方法;
3、Controller
添加logout和clearCookie方法;
4、退出URL放行
认证服务默认都要校验用户的身份信息,这里需要将退出url放行。
在WebSecurityConfig类中重写 configure(WebSecurity web)方法,如下:
@Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/userlogin","/userlogout","/userjwt"); }
测试:点击退出查看效果
POST:http://ucenter.xuecheng.com/openapi/auth/userlogout
Zuul网关
网关的作用相当于一个过滤器、拦截器,它可以拦截多个系统的请求。
Spring Cloud Zuul是整合Netflix公司的Zuul开源项目实现的微服务网关,它实现了请求路由、负载均衡、校验过滤等功能。
官方:https://github.com/Netflix/zuul
服务网关是在微服务前边设置一道屏障,请求先到服务网关,网关会对请求进行过滤、校验、路由等处理。有了服务网关可以提高微服务的安全性和网关校验请求的合法性,当请求不合法时将被拦截,拒绝访问
Zuul与Nginx怎么配合使用?
Zuul与Nginx在实际项目中需要配合使用,如下图,Nginx的作用是反向代理、负载均衡,Zuul的作用是保障微服务的安全访问,拦截微服务请求,校验合法性及负载均衡。
客户发送请求,首先通过Nginx进行请求分发到各个微服务,其中zuul在其中先进行拦截。
Zuul网关具有代理的功能,根据请求的url转发到微服务,如下图:
客户端请求网关/api/learning,通过路由转发到/learning
客户端请求网关/api/course,通过路由转发到/course
搭建网关工程
1、创建xc-govern-gateway工程
2、启动类上添加@EnableZuulProxy注解
3、yml文件中添加以下内容
zuul: routes: manage-course: path: /course/** serviceId: xc-service-manage-course #微服务名称,网关会从eureka中获取该服务名称下的服务实例的地址 # 例子:将请求转发到http://localhost:31200/course #url: http://www.baidu.com #也可指定url,此url也可以是外网地址\ strip-prefix: false #true:代理转发时去掉前缀,false:代理转发时不去掉前缀 sensitiveHeaders: #默认zuul会屏蔽cookie,cookie不会传到下游服务,这里设置为空则取消默认的黑名单,如果设置了具体的头信息则不会传到下游服务 # ignoredHeaders: 默认为空表示不过虑任何头
serviceId:推荐使用serviceId,zuul会从Eureka中找到服务id对应的ip和端口。
strip-prefix: false #true:代理转发时去掉前缀,false:代理转发时不去掉前缀,例如,为true请 求/course/coursebase/get/…,代理转发到/coursebase/get/,如果为false则代理转发到/course/coursebase/get
sensitiveHeaders:敏感头设置,默认会过虑掉cookie,这里设置为空表示不过虑
ignoredHeaders:可以设置过虑的头信息,默认为空表示不过虑任何头
4、测试
请求:http://localhost:50201/api/course/coursepic/list/4028e58161bd22e60161bd23672a0001
查询课程图片信息
http://localhost:50201/api是网关地址,通过路由转发到xc-service-manage-course服务获取到请求地址http://localhost:50201,所以要使用到网关,在请求地址上添加/api即可
由于课程管理已经添加了授课拦截,这里为了测试网关功能暂时将“/course/coursepic/list”的url排除认证
在课程管理服务的ResourceServerConfig类中添加"/course/coursepic/list/*",代码如下:
@Override public void configure(HttpSecurity http) throws Exception { //所有请求必须认证通过 http.authorizeRequests() //下边的路径放行 .antMatchers("/v2/api-docs", "/swagger-resources/configuration/ui", "/swagger-resources","/swagger-resources/configuration/security", "/swagger-ui.html","/webjars/**","/course/coursepic/list/**").permitAll() .anyRequest().authenticated(); }
5、yml文件中添加完整路由:
manage-cms: path: /cms/** serviceId: xc-service-manage-cms strip-prefix: false sensitiveHeaders: manage-sys: path: /sys/** serviceId: xc-service-manage-cms strip-prefix: false sensitiveHeaders: service-ucenter: path: /ucenter/** serviceId: xc-service-ucenter sensitiveHeaders: strip-prefix: false xc-service-manage-order: path: /order/** serviceId: xc-service-manage-order sensitiveHeaders: strip-prefix: false
过滤器
Zuul的核心就是过虑器,通过过虑器实现请求过虑,身份校验等。
ZuulFilter
自定义过虑器需要继承 ZuulFilter,ZuulFilter是一个抽象类,需要覆盖它的四个方法,如下:
1、shouldFilter:返回一个Boolean值,判断该过滤器是否需要执行。返回true表示要执行此过虑器,否则不执行。
2、run:过滤器的业务逻辑。
3、filterType:返回字符串代表过滤器的类型,如下
pre:请求在被路由之前执行
routing:在路由请求时调用
post:在routing和errror过滤器之后调用
error:处理请求时发生错误调用
4、filterOrder:此方法返回整型数值,通过此数值来定义过滤器的执行顺序,数字越小优先级越高。
测试
过虑所有请求,判断头部信息是否有Authorization,如果没有则拒绝访问,否则转发到微服务。
定义过虑器,使用@Component标识为bean。
编写过滤器测试类:LoginFilterTest
测试请求:http://localhost:50201/api/course/coursebase/get/4028e581617f945f01617f9dabc40000查询课程信息
1、Header中不设置Authorization
2、Header中设置Authorization
成功响应课程信息。
身份校验
本小节实现网关连接Redis校验令牌:
1、从cookie查询用户身份令牌是否存在,不存在则拒绝访问
2、从http header查询jwt令牌是否存在,不存在则拒绝访问
3、从Redis查询user_token令牌是否过期,过期则拒绝访问
1、在yml文件中配置redis参数:
redis: host: ${REDIS_HOST:127.0.0.1} port: ${REDIS_PORT:6379} timeout: 5000 #连接超时 毫秒 jedis: pool: maxActive: 3 maxIdle: 3 minIdle: 1 maxWait: -1 #连接池最大等行时间 -1没有限制
2、使用StringRedisTemplate查询key的有效期
在AuthService类中添加getTokenFromCookie、getJwtFromHeader和getExpire方法
说明:由于令牌存储时采用String序列化策略,所以这里用StringRedisTemplate来查询,使用RedisTemplate无法完成查询
3、定义LoginFilter类
4、配置代理
通过nginx转发到gateway,在www.xuecheng.com虚拟主机来配置
#微服务网关 upstream api_server_pool{ server 127.0.0.1:50201 weight=10; } #微服务网关 location /api { proxy_pass http://api_server_pool; }
使用postman测试:
Get请求:http://www.xuecheng.com/api/course/coursebase/get/4028e581617f945f01617f9dabc40000
注意:这里通过网关请求了course/coursebase/get地址,课程管理url根据自己的开发情况去配置
1、正常流程测试
a、执行登录之前向cookie写入身份令牌uid
Post请求:http://ucenter.xuecheng.com/openapi/auth/userlogin
并从redis获取jwt令牌的内容
b、手动在postman添加header
成功查询:
2、异常流程测试
手动删除header或清除cookie观察测试结果
Spring Boot 面试精讲