SpringSecurity+Oauth2+JWT安全框架(二)


一、登录接口具体实现

完成上一篇测试以后,表示我们搭建的认证服务器可以生成令牌,这时候我们需要再此基础上写登录逻辑

登录接口

前端post提交账号、密码等,用户身份效验通过,生成令牌,并将令牌存储到redis当中,具体业务流程图如下

 
创建AuthControllerApi.java
@Api(tags = "用户认证API接口", description = "用户登录认证接口")
public interface AuthControllerApi {
    @ApiOperation(value = "登录")
    @PostMapping("/userLogin")
    public ResultVo login(LoginRequest loginRequest);

    @ApiOperation(value = "退出")
    public ResultVo logout();
}

 

创建AuthController.java

@RestController
public class AuthController implements AuthControllerApi{
    @Autowired
    private AuthService authService;

    @Autowired
    private SecurityAuth securityAuth;

    @Override
    @PostMapping("/userLogin")
    public ResultVo login(LoginRequest loginRequest) {
        //申请令牌
        ResultVo resultVo=authService.login(loginRequest,securityAuth.getClientId(),securityAuth.getClientSecret());
        return resultVo;
    }
}

 

创建AuthService.java

@Service
public class AuthService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private SecurityAuth securityAuth;

    public ResultVo login(LoginRequest loginRequest, String clientId, String clientSecret) {
        //用户账号和密码的非空判断
        if (!Exist.isExists(loginRequest) || !Exist.isExists(loginRequest.getPassword())) {
            return ResultVo.response(AuthKeFeiEnums.FAIL_NULL_PASSWORD);
        }
        if (!Exist.isExists(loginRequest) || !Exist.isExists(loginRequest.getUsername())) {
            return ResultVo.response(AuthKeFeiEnums.FAIL_NULL_USERNAME);
        }
        //获取客户账号和密码
        String username = loginRequest.getUsername();
        String password = loginRequest.getPassword();
        //请求security获取令牌
        AuthToken authToken = applyToken(username, password, clientId, clientSecret);
        if (!Exist.isExists(authToken)) {
            return ResultVo.response(AuthKeFeiEnums.FAIL_AUTH_APPLYTOKEN_NULL);
        }

        //获取用户身份令牌
        String access_token = authToken.getAccess_token();
        //存储到redis中的内容
        String jsonString = JSON.toJSONString(authToken);
        //将令牌存储到redis当中
        boolean result = this.saveToken(access_token, jsonString, securityAuth.getTokenValiditySeconds());
        if (!result) {
            return ResultVo.response(AuthKeFeiEnums.FAIL_SAVE_AUTH_TOKEN);
        }
        //将令牌存储到cookie
        addCookie(securityAuth.getCookieDomain(), "/", "uid", access_token, securityAuth.getCookieMaxAge(), false);
        return ResultVo.response(AuthKeFeiEnums.SUCCESS, access_token);
    }

 

/**
     * @param clientId     客户端账号
     * @param clientSecret 客户端密码
     * @return java.lang.String
     * @author GuFei
     * <获取httpbasic的串>
     * @date 2019/8/28 11:52
     * @version V1.0
     */
    private String getHttpBasic(String clientId, String clientSecret) {
        String ss = clientId + ":" + clientSecret;
        byte[] encode = Base64Utils.encode(ss.getBytes());
        //Basic后面要空一个
        return "Basic " + new String(encode);
    }

    /**
     * @param username     用户账号
     * @param password     用户密码
     * @param clientId     客户端账号
     * @param clientSecret 客户端密码
     * @return com.kefei.fremawork.dto.po.security.AuthToken
     * @author GuFei
     * <申请令牌>
     * @date 2019/8/28 11:53
     * @version V1.0
     */
    private AuthToken applyToken(String username, String password, String clientId, String clientSecret) {
        RestTemplate restTemplate = new RestTemplate();
        //定义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(securityAuth.getAccessTokenUri(), 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();
        //访问令牌(jwt)
        String jwt_token = (String) bodyMap.get("access_token");
        //刷新令牌(jwt)
        String refresh_token = (String) bodyMap.get("refresh_token");
        //jti,作为用户的身份标识
        String access_token = (String) bodyMap.get("jti");
        authToken.setJwt_token(jwt_token);
        authToken.setAccess_token(access_token);
        authToken.setRefresh_token(refresh_token);
        return authToken;
    }

    /*
     * @author GuFei
     * <描述内容>
     * @date 2019/8/28 11:53
     * @param access_token 令牌
     * @param content 获取到令牌、刷新令牌、短令牌等信息
     * @param ttl 存储在redis中的有效时间
     * @return boolean
     * @version V1.0
     */
    private boolean saveToken(String access_token, String content, long ttl) {
        //令牌名称key
        String nameKey = RedisKeyGenerator.saveTokenKey(access_token);
        //保存令牌到redis,TimeUnit.SECONDS为设置过期时间,单位为秒
        stringRedisTemplate.boundValueOps(nameKey).set(content, ttl, TimeUnit.SECONDS);
        //获取过期时间
        Long expire = stringRedisTemplate.getExpire(nameKey);
        //如果大于0,表示存储成功,如果小于0则表示存储失败了
        return expire > 0;
    }

    /**
     * @param domain
     * @param path
     * @param name     cookie名字
     * @param value    cookie值
     * @param maxAge   cookie生命周期 以秒为单位
     * @param httpOnly
     * @return void
     * @author GuFei
     * <将令牌存储到cookie>
     * @date 2019/8/28 11:55
     * @version V1.0
     */
    private void addCookie(String domain, String path, String name,
                           String value, int maxAge, boolean httpOnly) {
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        Cookie cookie = new Cookie(name, value);
        cookie.setDomain(domain);
        cookie.setPath(path);
        cookie.setMaxAge(maxAge);
        cookie.setHttpOnly(httpOnly);
        response.addCookie(cookie);
    }
}

 

注意:在使用postman测试的时候,一切正常但是正式环境测试异常报401,Request Method是options类型,这是因为web前端在发送请求的时候,会先发送一个options请求,作为探测请求查看请求链接是否建立。

 

解决办法:

我们需要再资源服务里面找到继承ResourceServerConfigurerAdapter的实体类

 

给与放行

 

至此,我们完成了认证服务器的登录认证请求


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM