Security 认证流程分析(了解)
目前的登陆操作,也就是用户的认证操作,其实现主要基于Spring Security框架,其认证简易流程:
颁发登陆成功令牌
构建令牌存储对象
本次我们借助JWT(Json Web Token-是一种json格式)方式将用户相关信息进行组织和加密,并作为响应令牌(Token),从服务端响应到客户端,客户端接收到这个JWT令牌之后,将其保存在客户端(例如localStorage),然后携带令牌访问资源服务器,资源服务器获取并解析令牌的合法性,基于解析结果判定是否允许用户访问资源.
//创建令牌存储的服务配置
@Configuration
public class TokenConfig {
//为令牌设置一个口令,可以自定义设置
//生成的密匙需要此口令进行签名,拿到的令牌需要此口令进行验证
private String SIGNING_KEY= "auth";
//创建令牌存储TokenStore 对象,内部存储的是一个访问令牌转换器jwtAccessTokenConverterjwt对象
@Bean
public TokenStore tokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
//构建JWT令牌转换器方法,基于此方法返回创建好的令牌,解析令牌
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
//为创建 访问令牌转换器jwtAccessTokenConverter对象 设置签名的密钥
jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);
//返回设置好的令牌
return jwtAccessTokenConverter;
}
}
定义认证授权核心配置
第一步:在SecurityConfig中添加如下方法(创建AuthenticationManager 身份验证管理器对象,后面授权服务器会用到):
@Bean
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
第二步:配置认证和授权服务
/**
* 在这个对象中负责将所有的认证和授权配进行整合,例如
* 1)SpringSecurity (安全认证和授权)
* 2)TokenConfig(提供了令牌的生成,储存,校验)
* 3)Oauth2(定义了一套认证规范,例如给谁发令牌,发什么)
*/
//AuthorizationServerConfigurerAdapter 授权服务器配置器适配器
@Configuration
@AllArgsConstructor//构造全参函数方法
@EnableAuthorizationServer //开启认证和授权服务
public class Oauth2Config extends AuthorizationServerConfigurerAdapter{
//负责完成认证管理
private AuthenticationManager authenticationManager;
//服务获取用户信息
private UserDetailsService userDetailsService;
//TokenStore负责完成令牌创建,信息读取
private TokenStore tokenStore;
//负责密码加密匹配对象
private PasswordEncoder passwordEncoder;
//JWT令牌转换器(基于用户信息构建令牌,解析令牌)
private JwtAccessTokenConverter jwtAccessTokenConverter;
/**
* 认证方式配置
* @param endpoints
* @throws Exception
* */
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//super.configure(endpoints);
//操作endpoints的相关属性
//完成认证的对象
endpoints.authenticationManager(authenticationManager);
//访问数据库的对象。 认证需要两部分信息,一部分来自数据库,一部分来自客户端
endpoints.userDetailsService(userDetailsService);
//支持对什么请求进行认证? 默认只支持post方法,可以自行添加
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
//认证成功以后令牌如何存储?默认令牌是UUID.randomUUID(),并且默认存在内存中
endpoints.tokenServices(tokenServices());
}
//系统底层在完成认证以后会调用TokenServuce对象的相关方法
//获取TokenStore,基于TokenStore获取token对象
@Bean
public AuthorizationServerTokenServices tokenServices(){
//1.构建TokenService Token服务 对象(此对象提供了创建,获取,刷新token的方法)
DefaultTokenServices tokenServices = new DefaultTokenServices();
//2设置访问令牌过期了,是否支持通过令牌刷新机制延长有效时间,设置支持刷新令牌
tokenServices.setSupportRefreshToken(true);
//3.设置令牌生产和存贮策略
tokenServices.setTokenStore(tokenStore);
//4.设置令牌增强(允许设置令牌生成策略,默认令牌比较简单,没有事业务数据,
// 就是简单的随机字符,现在希望使用jwt策略)
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(
jwtAccessTokenConverter));
tokenServices.setTokenEnhancer(tokenEnhancerChain);
//设置访问令牌有效期时长,设置接入令牌有效期
tokenServices.setAccessTokenValiditySeconds(3600);
//设置“刷新令牌有效期”
tokenServices.setRefreshTokenValiditySeconds(3600*72);
//返回设置好的令牌服务
return tokenServices;
}
/**
* 假如入我们输入了用户名和密码,然后点提交,提交到哪呢?
* 临牌过去了,重写生成一个令牌
* 那个路径可以帮助我们重新生成?
* 这个方法就可以提供这个配置
* 对外提供认证,令牌刷新,校验等路径(url)
* @param security
* @throws Exception
* */
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//super.configure(security);
//设置security的属性,permitAll()是官方定义好的
//1.定义认证的url,"permitAll()"表示所有的url
security.tokenKeyAccess("permitAll()");
//2.定义检查url
security.checkTokenAccess("permitAll()");
//3.允许直接认证,直接通过表单方式提交认证
security.allowFormAuthenticationForClients();
}
/**
* 认证中心需要给所有的用户端的用户发令牌吗?
* 发令牌有一些规则的定义
* 客户端详情配置
* @param clients
* @throws Exception
* */
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//super.configure(clients);
clients.inMemory()
//定义客户端的id(客户端提交认证时需要这个id)
.withClient("gateway-client")
//定义秘钥(客户端提交用户信息时需要携带这个秘钥)
.secret(passwordEncoder.encode("123456"))
//定义作用范围(所有符合规则的客户端)
.scopes("all")
//认证的模式(允许客户端基于密码方法,刷新令牌方式实现认证)
.authorizedGrantTypes("password","refresh_token");
}
}
配置网关认证的URL
- id: router02
uri: lb://sca-auth
predicates:
#- Path=/auth/login/** #没要令牌之前,以前是这样配置
- Path=/auth/oauth/** #微服务架构下,需要令牌,现在要这样配置
filters:
- StripPrefix=1
Postman访问测试
第一步:启动服务
依次启动sca-auth服务,sca-resource-gateway服务。
第二步:检测sca-auth服务控制台的Endpoints信息,例如:
第三步:打开postman进行登陆访问测试
登陆成功会在控制台显示令牌信息,例如:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzI3NDEzODMsInVzZXJfbmFtZSI6ImphY2siLCJhdXRob3JpdGllcyI6WyJzeXM6cmVzOmNyZWF0ZSIsInN5czpyZXM6cmV0cmllY2UiXSwianRpIjoiZjQyMGViMGYtZDFjNi00YTc2LWIyY2ItY2QzMDU4OTU3MDk4IiwiY2xpZW50X2lkIjoiZ2F0ZXdheS1jbGllbnQiLCJzY29wZSI6WyJhbGwiXX0.xatqAhlJMCAgU_wZK3M4himRjQFHjYcTCWmdIQO5aSU",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJqYWNrIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImY0MjBlYjBmLWQxYzYtNGE3Ni1iMmNiLWNkMzA1ODk1NzA5OCIsImV4cCI6MTYzMjk5Njk4MywiYXV0aG9yaXRpZXMiOlsic3lzOnJlczpjcmVhdGUiLCJzeXM6cmVzOnJldHJpZWNlIl0sImp0aSI6ImQ1MmFkMTE3LTAyMGYtNDM3OS1hNTcyLTQ3ZWRkZDJhODQwNCIsImNsaWVudF9pZCI6ImdhdGV3YXktY2xpZW50In0.Bw2pqzUWKeVErReRG2StYG-PI38r_n6orBhI-ZeNCdk",
"expires_in": 3599,
"scope": "all",
"jti": "f420eb0f-d1c6-4a76-b2cb-cd3058957098"
}
登陆页面登陆方法设计
登陆成功以后,将token存储到localStorage中,修改登录页面的doLogin方法,例如
覆盖原来网页的diLogin()方法
doLogin() {
//1.定义url
let url = "http://localhost:9000/auth/oauth/token"
//2.定义参数
let params = new URLSearchParams()
params.append('username',this.username);
params.append('password',this.password);
params.append("client_id","gateway-client");
params.append("client_secret","123456");
params.append("grant_type","password");
//3.发送异步请求
axios.post(url, params).then((response) => {
alert("login ok");
let result=response.data;
localStorage.setItem("accessToken",result.access_token);
location.href="/fileupload.html";
}).catch((error)=>{
console.log(error);
})
}
页面写好以后,启动服务进行登录测试即可。