乐优商城项目总结——11.5 授权中心(JWT和RSA实现)
作者:互联网
创建授权中心
上一篇介绍了JWT和RSA,这里就用这两种技术实现授权中心。
先在网关(zuul)中配置一下,前面几篇没讲到
yml:
最后一段是授权中心的路由。
zuul:
prefix: /api # 添加路由前缀,是全局的前缀,请求都得加上api
routes:
item-service: /item/** #可以省略,但是默认是/item-service/**,这样改下路径,前面是eureka的服务id,后面是映射路径
search-service: /search/**
user-service: /user/**
auth-service: /auth/**
授权中心也是一个聚合工程,common里放的是工具类。
common里的类
首先是用户的实体类,用来存登录用户的信息,这些数据以后放到载荷里。
注解使用的是lombok:
set,get 方法
无参构造
全参构造
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {
private Long id;
private String username;
}
然后就是util包,太多了,略。
具体业务
流程图:
也就是说在授权中心给权限,在网关鉴权,在有的微服务也要鉴权。
1,先从授权中心讲:
授权中心要在yml中配置jwt的相关配置。
yml:
登录校验的密钥(secret)自己设置,我胡乱打的。
ly:
jwt:
secret: afjdkjgkdsglkas # 登录校验的密钥
pubKeyPath: E:/Java_IOTest/leyoumiyao/rsa.pub # 公钥地址
priKeyPath: E:/Java_IOTest/leyoumiyao/rsa.pri # 私钥地址
expire: 30 # 过期时间,单位分钟
cookieName: LY_TOKEN
cookieMaxAge: 1800 #cookie过期时间
controller:
登录的时候写一个方法给用户授权,生成jwt类型的token。
在进行一些操作的时候进行校验用户的登陆时间(在controller就完成了)。
@Controller
@EnableConfigurationProperties(JwtProperties.class)
public class AuthController {
@Autowired
private AuthService authService;
@Autowired
private JwtProperties prop;
/**
* 登录授权
*
* @param username
* @param password
* @return
*/
@PostMapping("accredit")
public ResponseEntity<Void> authentication(
@RequestParam("username") String username,
@RequestParam("password") String password,
HttpServletRequest request,
HttpServletResponse response) {
// 登录校验
String token = this.authService.accredit(username, password);
if (StringUtils.isBlank(token)) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
// 将token写入cookie,并指定httpOnly为true,防止通过JS获取和修改
CookieUtils.setCookie(request, response, prop.getCookieName(),
token, prop.getCookieMaxAge(), null, true);
return ResponseEntity.ok().build();
}
/**
* 验证用户登录状态
*
* @param token
* @return
*/
@GetMapping("verify")
public ResponseEntity<UserInfo> verify(@CookieValue("LY_TOKEN") String token
, HttpServletRequest request, HttpServletResponse response) {
// 解析token
try {
UserInfo info = JwtUtils.getInfoFromToken(token, prop.getPublicKey());
// 刷新token
String newToken = JwtUtils.generateToken(info, prop.getPrivateKey(), prop.getExpire());
// 歇入cookie中
CookieUtils.setCookie(request, response, prop.getCookieName(),
newToken, prop.getCookieMaxAge(), null, true);
// 已登录,返回用户信息
return ResponseEntity.ok(info);
} catch (Exception e) {
// token已过期,抛出异常
throw new LyException(ExceptionEnum.UNAUTHORIZED);
}
}
}
service:
@Slf4j
@Service
@EnableConfigurationProperties(JwtProperties.class)
public class AuthService {
@Autowired
private UserClient userClient;
@Autowired
private JwtProperties jwtProperties;
public String accredit(String username, String password) {
// 1,根据用户名和密码查询
User user = userClient.queryUser(username, password);
// 2,判断user
if (user == null) {
log.info("用户信息不存在,{}", username);
return null;
}
// 3,jwtUtils生成jwt类型的token
UserInfo userInfo = new UserInfo();
userInfo.setId(user.getId());
userInfo.setUsername(user.getUsername());
try {
return JwtUtils.generateToken(userInfo, jwtProperties.getPrivateKey(), jwtProperties.getExpire());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
2,再讲网关(zuul)里的校验:
在这里面yml也要进行一些配置
因为只做校验,只需要公钥就可以
有的操作不需要登录也应该让用户能操作,比如查找商品,看你个商品你还让我登录,要我我就直接不买了。所以添加白名单allowPaths。
ly:
jwt:
pubKeyPath: E:/Java_IOTest/leyoumiyao/rsa.pub # 公钥地址
cookieName: LY_TOKEN
filter:
allowPaths:
- /api/auth
- /api/search
- /api/user/register
- /api/user/check
- /api/user/code
- /api/item
zuul网关过滤器里实现校验:
在这里面有四个重写方法:
- 第一个是过滤类型,要在里面说明什么时候过滤,是在刚进网关?进入网关之后? 指明什么时候过滤。
- 第二个是过滤顺序,在官方过滤器之前就-1,等等有很多类型。
- 第三个是要不要过滤,因为前面说了有白名单,所以在里面进行判断。
- 第四个是要对过滤的业务逻辑,如果不不符和要求直接拦截下来。
@Component
@EnableConfigurationProperties({JwtProperties.class, FilterProperties.class})
public class AuthFilter extends ZuulFilter {
@Autowired
private JwtProperties jwtProperties;
@Autowired
private FilterProperties filterProperties;
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;//过滤器类型,前置过滤
}
@Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;//过滤器顺序,在官方过滤器前面,所以-1
}
@Override
public boolean shouldFilter() { //要不要过滤
// 设置白名单
// 获取上下文
RequestContext context = RequestContext.getCurrentContext();
// 获取request
HttpServletRequest request = context.getRequest();
// 获取请求路径
String path = request.getRequestURI();
// 允许放行,应该返回false,因为这个方法是(是否过滤)
return !isAllowPath(path);
}
private boolean isAllowPath(String path) {
for (String allowPath : filterProperties.getAllowPaths()) {
// 判断是否允许
if (path.startsWith(allowPath)) {
return true;
}
}
return false;
}
/**
* 过滤器逻辑
*
* @return
* @throws ZuulException
*/
@Override
public Object run() {
// 获取上下文
RequestContext context = RequestContext.getCurrentContext();
// 获取request
HttpServletRequest request = context.getRequest();
// 获取cookie里的token
String token = CookieUtils.getCookieValue(request, jwtProperties.getCookieName());
// 解析token
try {
UserInfo user = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey());
} catch (Exception e) {
// 解析token失败,未登录,拦截
context.setSendZuulResponse(false);
// 设置状态码为403
context.setResponseStatusCode(403);
}
// 校验权限
return null;
}
}
3,最后讲如何获取获取JWT里的信息:
跟网关里的校验差不多。
yml
只需要解析,所以只用知道公钥的地址
ly:
jwt:
pubKeyPath: E:/Java_IOTest/leyoumiyao/rsa.pub # 公钥地址
cookieName: LY_TOKEN
到了下单这一步,就需要获取用户的信息,这里用的是springMVC拦截器。
需要注意的是threadlocal,threadlocal是一个thread的局部变量,可以在当前线程访问资源时共享访问资源,可以把用户信息放到threadlocal里面,当线程用完后记得销毁,虽然它会自动销毁,但是用的是tomcat的线程池,线程用完是归还,但是不会销毁,所以要手动销毁。
**
* 添加拦截器解析用户信息
* springMVC拦截,如果只需要某一个方法,就不用实现全部三个方法,继承HandlerInterceptorAdapter就可以重写其中一个方法
*/
@Component
@EnableConfigurationProperties(JwtProperties.class)
public class CartFilter extends HandlerInterceptorAdapter {
@Autowired
private JwtProperties jwtProperties;
/**
* threadlocal是一个thread的局部变量,可以在当前线程访问资源时共享访问资源
*/
private static final ThreadLocal<UserInfo> THREAD_LOCAL = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取cookie的token
String token = CookieUtils.getCookieValue(request, jwtProperties.getCookieName());
// 解析token,获取用户信息,因为如果没有正确的token就不会到达这里,在网关那里就已经拦截了
UserInfo userInfo = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey());
if (userInfo == null) {
return false;
}
// 放入到threadlocal
THREAD_LOCAL.set(userInfo);
return true;
}
/**
* 获取userInfo的方法
*
* @return
*/
public static UserInfo getUserInfo() {
return THREAD_LOCAL.get();
}
/**
* 在完成方法的时候删除线程局部变量
* 因为:虽然他会自动删除,但是此时用的是tomcat的线程池,用完会回收但不会删除当前线程,所以要手动删除
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
THREAD_LOCAL.remove();
}
}
有了用户信息,就该让他掏钱了。
OVER
标签:return,String,11.5,乐优,JWT,request,private,token,public 来源: https://blog.csdn.net/qq_40586805/article/details/98080217