续 实现授权服务器
编写登录用配置类
上次课我们已经在sys模块中编写准备好了3个Rest接口
1.根据用户名获得用户对象
2.根据用户id获得所有权限
3.根据用户id获得所有角色
下面要在knows-auth模块编写登录配置类UserDetailsServiceImpl来实现根据用户名返回Spring-Security框架需要的UserDetails对象的方法以便支持登录验证
auth模块创建service包
包中创建UserDetailsServiceImpl类
和单体portal项目登录配置思路一致,但是用户相关的信息要修改为Ribbon调用,最终代码如下
// 当前登录配置类需要保存到Spring容器
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
@Resource
private RestTemplate restTemplate;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1.根据用户名获得用户对象
String url="http://sys-service/v1/auth/user?username={1}";
User user=restTemplate
.getForObject(url , User.class , username);
// 2.判断查询出来的用户是否存在
if(user==null){
// 如果用户对象为空,抛出异常表示登录失败
//throw new ServiceException("用户名密码错误!");
throw new UsernameNotFoundException("用户名密码错误!");
}
// 3.根据用户id查询用户所有权限
url="http://sys-service/v1/auth/permissions?id={1}";
// Ribbon请求的控制器方法返回值为List时
// 要使用该List泛型类型的数组来接收
Permission[] permissions=restTemplate
.getForObject(url , Permission[].class , user.getId());
// 4.根据用户id查询用户所有角色
url="http://sys-service/v1/auth/roles?id={1}";
Role[] roles=restTemplate
.getForObject(url , Role[].class , user.getId());
// 5.将权限和角色保存在auth数组中
String[] auth=new String[permissions.length+roles.length];
// 分别遍历权限和角色数组,将权限和角色的名称保存在auth数组中
int i=0;
for(Permission p: permissions){
auth[i++]=p.getName();
}
for(Role r: roles){
auth[i++]=r.getName();
}
// 6.创建UserDetails对象
UserDetails details= org.springframework.security
.core.userdetails.User.builder()
.username(user.getUsername())
.password(user.getPassword())
.authorities(auth)
.accountLocked(user.getLocked()==1) //是否锁定(false表示不锁定)
.disabled(user.getEnabled()==0) // 是否可用 (false表示可用)
.build();
// 7.千万别忘了返回 details!!!
return details;
}
}
上面的代码是关键的登录配置
必须测试一下才能安心使用
auth模块的test文件夹下编写测试代码如下
@Resource
UserDetailsServiceImpl userDetailsService;
@Test
void contextLoads() {
UserDetails details=userDetailsService
.loadUserByUsername("st2");
System.out.println(details);
// Nacos和sys先启动,在运行测试!
}
运行结果可能是:
org.springframework.security.core.userdetails.User@1bdf1: Username: st2;
Password: [PROTECTED];
Enabled: true;
AccountNonExpired: true;
credentialsNonExpired: true;
AccountNonLocked: true;
Granted Authorities: /index.html,/question/create,/question/detail,/question/uploadMultipleFile,ROLE_STUDENT
要保证Nacos启动并且sys模块启动
运行能输出用户信息表示一切正常
授权服务器核心配置
上面所有编写的类和代码都是为了核心配置做准备的
核心配置就是再创建一个类,这个类要继承一个父类
重写这个父类的三个方法,这三个方法的作用分别是
1.配置授权服务器的各种参数
2.配置客户端对应的各种权限
3.配置客户端允许使用的功能
security包中创建该类
AuthorizationServer(授权服务器)
@Configuration
// 这个注解表示当前类时Oauth2标准下实现的授权服务器配置类
// 表示启动授权服务器相关功能
@EnableAuthorizationServer
public class AuthorizationServer extends
AuthorizationServerConfigurerAdapter {
// 添加依赖注入的对象,它们大多是之前课程中向Spring容器中保存准备好的配置
// Spring-Security框架的授权管理器,Oauth2要使用
@Resource
private AuthenticationManager authenticationManager;
// 要登录肯定需要登录配置类
@Resource
private UserDetailsServiceImpl userDetailsService;
// 核心配置方法1:配置授权服务器的各种参数
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// Oauth2框架提供了很多控制器方法
// 所以我们配置的就是这些控制器方法运行的内容
// endpoints(端点)参数其实就是控制器方法的功能
// 配置登录时的授权管理器
endpoints.authenticationManager(authenticationManager)
// 设置登录配置类
.userDetailsService(userDetailsService)
// 配置登录允许的提交方式
.allowedTokenEndpointRequestMethods(HttpMethod.POST)
// 配置令牌生成器对象
.tokenServices(tokenService());
}
// 注入保存令牌配置的对象
@Resource
private TokenStore tokenStore;
// 添加客户端详情对象(框架提供的对象,不是我们写的)
@Resource
private ClientDetailsService clientDetailsService;
// Spring容器保存令牌生成器对象
@Bean
public AuthorizationServerTokenServices tokenService(){
// 实例化 令牌生成器对象
DefaultTokenServices services=
new DefaultTokenServices();
// 设置令牌保存策略
services.setTokenStore(tokenStore);
// 设置令牌有效期(单位是秒,1800就是半小时)
services.setAccessTokenValiditySeconds(3600);
// 指定生成令牌的客户端,设置客户端详情
services.setClientDetailsService(clientDetailsService);
// 最后返回令牌生成器对象
return services;
}
// 获得加密对象,用于下面方法中的加密操作
@Resource
private PasswordEncoder passwordEncoder;
// 核心配置方法2:配置客户端对应的各种权限
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 配置当前授权服务器支持的所有客户端
// 以及客户端的权限
// 因为我们现在只有一个<<达内知道>>项目,所以只配置这一个客户端
// 客户端比较少可以直接保存在内存
// 如果客户端比较多,可能需要连接数据库获得权限信息
clients.inMemory() // 内存中保存客户端信息
.withClient("knows") //定义客户端名称
// 客户端定义的加密密码
.secret(passwordEncoder.encode("123456"));
}
}
随笔
postman软件下载地址
https://www.postman.com/downloads/