上次分享到如何使用postman实现账号密码登陆,显然很复杂,传到的参数太多,一般我们只需要传两个参数即可,即 username和password这样就行了,况且以后做网关的话,也会分流到这个接口,于是这样的接口的开发势在必得。
1. 代码实现
1.1 创建请求类
三个参数,用户名,密码,验证码。
@Data
@ToString
public class LoginRequest {
String username;
String password;
String verifycode;
}
1.2 创建service
创建AuthService
,这里面分为一下几步
- 首先请求spring security申请令牌,这个就是请求我们之前的接口,考虑到线上可能集群部署,这里我们使用nacos使用注册中心,这样就可以负载均衡的调用。
- 获取好token,我们把用户身份令牌、jwt令牌存到redis中,并且设置过期时间,这个过期时间是从配置文件中读取。
注意的是:在请求的token时,要注意封装http basic认证,下面就将详细代码给出。
@Service
@Slf4j
public class AuthService {
@Value("${auth.tokenValiditySeconds}")
int tokenValiditySeconds;
@Autowired
LoadBalancerClient loadBalancerClient;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
RestTemplate restTemplate;
//用户认证申请令牌,将令牌存储到redis
public AuthToken login(String username, String password, String clientId, String clientSecret) {
//请求spring security申请令牌
AuthToken authToken = this.applyToken(username, password, clientId, clientSecret);
if (authToken == null) {
ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
}
//用户身份令牌
String access_token = authToken.getAccess_token();
//存储到redis中的内容
String jsonString = JSON.toJSONString(authToken);
//将令牌存储到redis
boolean result = this.saveToken(access_token, jsonString, tokenValiditySeconds);
if (!result) {
ExceptionCast.cast(AuthCode.AUTH_LOGIN_TOKEN_SAVEFAIL);
}
return authToken;
}
//存储到令牌到redis
/**
* @param access_token 用户身份令牌
* @param content 内容就是AuthToken对象的内容
* @param ttl 过期时间
* @return
*/
private boolean saveToken(String access_token, String content, long ttl) {
String key = "user_token:" + access_token;
stringRedisTemplate.boundValueOps(key).set(content, ttl, TimeUnit.SECONDS);
Long expire = stringRedisTemplate.getExpire(key, TimeUnit.SECONDS);
return expire > 0;
}
//申请令牌
private AuthToken applyToken(String username, String password, String clientId, String clientSecret) {
//从eureka中获取认证服务的地址(因为spring security在认证服务中)
//从eureka中获取认证服务的一个实例的地址
ServiceInstance serviceInstance = loadBalancerClient.choose("security-auth");
//此地址就是http://ip:port
URI uri = serviceInstance.getUri();
//令牌申请的地址 http://localhost:40400/auth/oauth/token
String authUrl = uri + "/auth/oauth/token";
//定义header
LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
String httpBasic = getHttpBasic(clientId, clientSecret);
header.add("Authorization", httpBasic);
//定义body
LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "password");
body.add("username", username);
body.add("password", password);
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, header);
//String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables
//设置restTemplate远程调用时候,对400和401不让报错,正确返回数据
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401) {
super.handleError(response);
}
}
});
ResponseEntity<Map> exchange = restTemplate.exchange(authUrl, HttpMethod.POST, httpEntity, Map.class);
//申请令牌信息
Map bodyMap = exchange.getBody();
if (bodyMap == null ||
bodyMap.get("access_token") == null ||
bodyMap.get("refresh_token") == null ||
bodyMap.get("jti") == null) {
return null;
}
AuthToken authToken = new AuthToken();
authToken.setAccess_token((String) bodyMap.get("jti"));//用户身份令牌
authToken.setRefresh_token((String) bodyMap.get("refresh_token"));//刷新令牌
authToken.setJwt_token((String) bodyMap.get("access_token"));//jwt令牌
return authToken;
}
//获取httpbasic的串
private String getHttpBasic(String clientId, String clientSecret) {
String string = clientId + ":" + clientSecret;
//将串进行base64编码
byte[] encode = Base64Utils.encode(string.getBytes());
return "Basic " + new String(encode);
}
}
1.3 创建controller
创建AuthController
,具体代码如下
@PostMapping("/userlogin")
public ApiResponse<String> login(LoginRequest loginRequest) {
if (loginRequest == null || StringUtils.isEmpty(loginRequest.getUsername())) {
ExceptionCast.cast(AuthCode.AUTH_USERNAME_NONE);
}
if (loginRequest == null || StringUtils.isEmpty(loginRequest.getPassword())) {
ExceptionCast.cast(AuthCode.AUTH_PASSWORD_NONE);
}
//账号
String username = loginRequest.getUsername();
//密码
String password = loginRequest.getPassword();
//申请令牌
AuthToken authToken = authService.login(username, password, clientId, clientSecret);
//用户身份令牌
String access_token = authToken.getAccess_token();
// //将令牌存储到cookie
this.saveCookie(access_token);
return new ApiResponse<>(access_token);
}
退出功能,删除cookie中token以及redis中的token。
//取出cookie中的用户身份令牌
String uid = getTokenFormCookie();
//删除redis中的token
boolean result = authService.delToken(uid);
//清除cookie
this.clearCookie(uid);
2. 测试功能
2.1 测试登陆
启动项目,发送POST请求。
http://batman.com:40400/auth/userlogin
,可以看到返回用户token,还可以查看cookie也已经写入。
返回结果为
{
"code": 0,
"message": "成功",
"data": "c654cef9-9e96-45d7-a230-25eb8edd856e"
}
查看redis,可以看到身份token和jwt的token都存到了redis中。
2.2 测试退出
POST请求:http://batman.com:40400/auth/userlogout
这是可以看下redis中token是否已经删除。