登录功能(接上一篇)

用户授权业务流程
在这里插入图片描述
业务流程说明如下:
1、用户认证通过,认证服务向浏览器cookie写入token( 身份令牌)
2、前端携带token请求用户中心服务获取jwt令牌
前端获取到jwt令牌解析,并存储在sessionStorage
3、前端携带cookie中的身份令牌及jwt令牌访问资源服务
前端请求资源服务需要携带两个token,一个是cookie中的身份令牌,一个是http header中的jwt
前端请求资源服务前在http header上添加jwt请求资源
4、网关校验token的合法性
用户请求必须携带身份令牌和jwt令牌
网关校验redis中user_token的有效期,已过期则要求用户重新登录
5、资源服务校验jwt的合法性并进行授权
资源服务校验jwt令牌,完成授权,拥有权限的方法正常执行,没有权限的方法将拒绝访问。

一、认证服务查询数据库
认证服务根据数据库中的用户信息去校验用户的身份,即校验账号和密码是否匹配。 认证服务不直接连接数据库,而是通过用户中心服务去查询用户中心数据库。
完整的流程图如下:
在这里插入图片描述
创建用户中心工程xc-service-ucenter
用户中心对外提供如下接口:
根据账号查询用户信息

@Api(value = "用户中心",description = "用户中心管理") 
public interface UcenterControllerApi { 
	public XcUserExt getUserext(String username); 
}
@Data 
@ToString 
public class XcUserExt extends XcUser { 
	//权限信息 
	private List<XcMenu> permissions; 
	//企业信息 
	private String companyId; 
}

认证服务需要远程调用用户中心服务查询用户,在认证服务中创建Feign客户端。

@FeignClient(value = XcServiceList.XC_SERVICE_UCENTER) 
public interface UserClient { 
	@GetMapping("/ucenter/getuserext") 
	public XcUserExt getUserext(@RequestParam("username") String username); 
}

认证服务调用spring security接口申请令牌,spring security接口会调用UserDetailsServiceImpl从数据库查询用户,如果查询不到则返回 NULL,表示不存在;在UserDetailsServiceImpl中将正确的密码返回, spring security 会自动去比对输入密码的正确性。
修改UserDetailsServiceImpl的loadUserByUsername方法,调用Ucenter服务的查询用户接口:

@Service 
public class UserDetailsServiceImpl implements UserDetailsService { 

	@Autowired 
	UserClient userClient; 
	
	@Override 
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 
		//取出身份,如果身份为空说明没有认证 
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 
		//没有认证统一采用httpbasic认证,httpbasic中存储了client_id和client_secret,开始认证 client_id和client_secret 
		if(authentication==null){ 
			ClientDetails clientDetails = clientDetailsService.loadClientByClientId(username); 
			if(clientDetails!=null){ 
				//密码 
				String clientSecret = clientDetails.getClientSecret(); 
				return new User(username,clientSecret,AuthorityUtils.commaSeparatedStringToAuthorityList("")); 
			} 
		}
		if (StringUtils.isEmpty(username)) { return null; }
		//请求ucenter查询用户 
		XcUserExt userext = userClient.getUserext(username); 
		if(userext == null){ 
			//返回NULL表示用户不存在,Spring Security会抛出异常 
			return null; 
		}
		//从数据库查询用户正确的密码,Spring Security会去比对输入密码的正确性 
		String password = userext.getPassword(); 
		String user_permission_string = ""; 
		UserJwt userDetails = new UserJwt(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList(user_permission_string)); 
		//用户id 
		userDetails.setId(userext.getId()); 
		//用户名称 
		userDetails.setName(userext.getName()); 
		//用户头像 
		userDetails.setUserpic(userext.getUserpic()); 
		//用户所属企业id 
		userDetails.setCompanyId(userext.getCompanyId()); 
		return userDetails; 
	} 
	
}

二、 jwt查询接口
认证服务对外提供jwt查询接口,流程如下:
1、客户端携带cookie中的身份令牌请求认证服务获取jwt 。
2、认证服务根据身份令牌从redis中查询jwt令牌并返回给客户端。
身份令牌从哪来?
该项目用jti作为用户的身份令牌。
在这里插入图片描述
向Oauth2申请令牌会返回上图的数据。

三、用户退出
用户退出只需要: 1、删除redis中的token。 2、删除cookie中的token。

四、Zuul网关
搭建网关工程
创建网关工程(xc-govern-gateway),注意在启动类上使用@EnableZuulProxy注解标识此工程为Zuul网关。

路由配置
在这里插入图片描述
客户端请求网关/api/learning,通过路由转发到/learning ;客户端请求网关/api/course,通过路由转发到/course。

身份校验
1、从cookie查询用户身份令牌是否存在,不存在则拒绝访问 。
2、从http header查询jwt令牌是否存在,不存在则拒绝访问。
3、从Redis查询user_token令牌是否过期,过期则拒绝访问。

五、方法授权
方法授权要完成的是资源服务根据jwt令牌完成对方法的授权,具体流程如下:
1、生成Jwt令牌时在令牌中写入用户所拥有的权限
我们给每个权限起个名字,例如某个用户拥有如下权限:
course_find_list:课程查询
course_pic_list:课程图片查询
2、在资源服务方法上添加注解PreAuthorize,并指定此方法所需要的权限
例如下边是课程管理接口方法的授权配置,它就表示要执行这个方法需要拥有course_find_list权限。

@PreAuthorize("hasAuthority('course_find_list')") 
@Override 
public QueryResult<CourseInfo> findCourseList(@PathVariable("page") int page, @PathVariable("size") int size, CourseListRequest courseListRequest)

3、当请求有权限的方法时正常访问
4、当请求没有权限的方法时则拒绝访问

jwt令牌包含权限。
动态查询用户权限:
1、管理员给用户分配权限,权限数据写到数据库中。 2、认证服务在进行用户认证时从数据库读取用户的权限数据(动态数据)。
在这里插入图片描述
xc_user:用户表,存储了系统用户信息,用户类型包括:学生、老师、管理员等
xc_role:角色表,存储了系统的角色信息,学生、老师、教学管理员、系统管理员等。
xc_user_role:用户角色表,一个用户可拥有多个角色,一个角色可被多个用户所拥有
xc_menu:模块表,记录了菜单及菜单下的权限
xc_permission:角色权限表,一个角色可拥有多个权限,一个权限可被多个角色所拥有

修改认证服务的UserDetailServiceImpl,查询用户的权限,并拼接权限串,代码如下:

...... 
	//请求ucenter查询用户
	XcUserExt userext = userClient.getUserext(username); 
	if(userext == null){ 
		//返回NULL表示用户不存在,Spring Security会抛出异常 
		return null; 
	}
	//从数据库查询用户正确的密码,Spring Security会去比对输入密码的正确性 
	String password = userext.getPassword(); 
	//指定用户的权限 
	List<String> permissionList = new ArrayList<>();  
	//取出用户权限 
	List<XcMenu> permissions = userext.getPermissions(); 
	for(XcMenu xcMenu:permissions){ 
		permissionList.add(xcMenu.getCode()); 
	}
......

Oauth2生成jwt令牌时应该会根据UserDetails生成,所以用户权限信息会包含在生成的jwt令牌里。

六、细粒度授权
什么是细粒度授权?
细粒度授权也叫数据范围授权,即不同的用户所拥有的操作权限相同,但是能够操作的数据范围是不一样的。一个例子:用户A和用户B都是教学机构,他们都拥有“我的课程”权限,但是两个用户所查询到的数据是不一样的。
本项目有哪些细粒度授权?
比如: 我的课程,教学机构只允许查询本教学机构下的课程信息。 我的选课,学生只允许查询自己所选课。
如何实现细粒度授权?
细粒度授权涉及到不同的业务逻辑,通常在service层实现,根据不同的用户进行校验,根据不同的参数查询不同的 数据或操作不同的数据。

我的课程查询,细粒度授权过程如下:
1)获取当前登录的用户Id
2)得到用户所属教育机构的Id
3)查询该教学机构下的课程信息
最终实现了用户只允许查询自己机构的课程信息。

获取当前用户
从header中取出JWT令牌,并解析JWT令牌的内容。具体方法封装成工具类Oauth2Util。代码见讲义。

七、微服务之间认证
微服务之间进行调用时需携带JWT。
可以使用Feign 拦截器。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值