Серия Spring Security Oauth2 (5)

задняя часть

Резюме:

В этой статье в основном рассказывается об использовании oauth2 внутри компании, чтобы меня запомнили.Изначально этот цикл статей не собирался обновлять, но компании нужно было запомнить реализацию этой функции, поэтому мне пришлось потратить 2 дня на изучение исходный код. Специально для всех.

Реализация функции «Запомнить меня»

Пожалуйста, обратитесь кCommunity Spring Security от вводной до расширенной серии руководствизСедьмой анализ исходного кода Spring Security: Spring Security запомнить меня

вопрос:

Во-первых, в разработке люди используют ajax для передачи данных.ThymeleafТехнология может быть не такой распространенной.В процессе запоминания моей реализации функции будет выполняться процесс перехода страницы авторизации.Потому что в предыдущей статье я использовал стойку регистрации для генерации формы формы для автоматического перехода.Конкретный код следующим образом:


@RequestMapping({ "/oauth/my_approval"})
    @ResponseBody
    public JSONObject getAccessConfirmation(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        @SuppressWarnings("unchecked")
        Map scopes = (Map) (model.containsKey("scopes") ? model.get("scopes") : request.getAttribute("scopes"));
        List scopeList = new ArrayList<>();
        for (String scope : scopes.keySet()) {
            scopeList.add(scope);
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("scopeList",scopeList);
        return jsonObject;
    }


function auth(){
        //定义一个form表单
        var form=$("<form>");
        $(document.body).append(form);
        form.attr("method","post");
        form.attr("action","../oauth/authorize");
        var inputUser=$("<input>");
        inputUser.attr("type","hidden");
        inputUser.attr("name","user_oauth_approval");
        inputUser.attr("value","true");
        for(var i = 0;i<scopeList.length;i++){
            var input = $("<input>");
            input.attr("type","hidden");
            input.attr("name",""+scopeList[i]+"")
            input.attr("value","true");
            form.append(input);
        }
        form.append(inputUser);
        form.submit();//表单提交
    }

Для получения дополнительной информации, пожалуйста, обратитесь к моему github, связанному сspring4allсвязанный код
Внимательные люди обнаружат, что этот переход вызван событием щелчка в предыдущем абзаце.Если это функция «запомнить меня», то она не будет автоматически логиниться через триггер события внешнего интерфейса, а затем приходить в это авторизованное место.В этом случае, мы не можем этого достичь./oauth/authorizeпроцесс сертификации. Исходный код ссылки выглядит следующим образом:


 //..........省略相关代码

@FrameworkEndpoint
@SessionAttributes({"authorizationRequest"})
public class AuthorizationEndpoint extends AbstractEndpoint {
    private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
    private RedirectResolver redirectResolver = new DefaultRedirectResolver();
    private UserApprovalHandler userApprovalHandler = new DefaultUserApprovalHandler();
    private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
    private OAuth2RequestValidator oauth2RequestValidator = new DefaultOAuth2RequestValidator();
    private String userApprovalPage = "forward:/oauth/confirm_access";
    private String errorPage = "forward:/oauth/error";
    private Object implicitLock = new Object();

    public AuthorizationEndpoint() {
    }

    public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
        this.sessionAttributeStore = sessionAttributeStore;
    }

    public void setErrorPage(String errorPage) {
        this.errorPage = errorPage;
    }

    @RequestMapping({"/oauth/authorize"})
    public ModelAndView authorize(Map model, @RequestParam Map parameters, SessionStatus sessionStatus, Principal principal) {
        AuthorizationRequest authorizationRequest = this.getOAuth2RequestFactory().createAuthorizationRequest(parameters);
        Set responseTypes = authorizationRequest.getResponseTypes();
        if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
            throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
        } else if (authorizationRequest.getClientId() == null) {
            throw new InvalidClientException("A client id must be provided");
        } else {
            try {
                if (principal instanceof Authentication && ((Authentication)principal).isAuthenticated()) {
                    ClientDetails client = this.getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
                    String redirectUriParameter = (String)authorizationRequest.getRequestParameters().get("redirect_uri");
                    String resolvedRedirect = this.redirectResolver.resolveRedirect(redirectUriParameter, client);
                    if (!StringUtils.hasText(resolvedRedirect)) {
                        throw new RedirectMismatchException("A redirectUri must be either supplied or preconfigured in the ClientDetails");
                    } else {
                        authorizationRequest.setRedirectUri(resolvedRedirect);
                        this.oauth2RequestValidator.validateScope(authorizationRequest, client);
                        authorizationRequest = this.userApprovalHandler.checkForPreApproval(authorizationRequest, (Authentication)principal);
                        boolean approved = this.userApprovalHandler.isApproved(authorizationRequest, (Authentication)principal);
                        authorizationRequest.setApproved(approved);
                        if (authorizationRequest.isApproved()) {
                            if (responseTypes.contains("token")) {
                                return this.getImplicitGrantResponse(authorizationRequest);
                            }

                            if (responseTypes.contains("code")) {
                                return new ModelAndView(this.getAuthorizationCodeResponse(authorizationRequest, (Authentication)principal));
                            }
                        }

                        model.put("authorizationRequest", authorizationRequest);
                        return this.getUserApprovalPageResponse(model, authorizationRequest, (Authentication)principal);
                    }
                } else {
                    throw new InsufficientAuthenticationException("User must be authenticated with Spring Security before authorization can be completed.");
                }
            } catch (RuntimeException var11) {
                sessionStatus.setComplete();
                throw var11;
            }
        }
    }

    @RequestMapping(
        value = {"/oauth/authorize"},
        method = {RequestMethod.POST},
        params = {"user_oauth_approval"}
    )
    public View approveOrDeny(@RequestParam Map approvalParameters, Map model, SessionStatus sessionStatus, Principal principal) {
        if (!(principal instanceof Authentication)) {
            sessionStatus.setComplete();
            throw new InsufficientAuthenticationException("User must be authenticated with Spring Security before authorizing an access token.");
        } else {
            AuthorizationRequest authorizationRequest = (AuthorizationRequest)model.get("authorizationRequest");
            if (authorizationRequest == null) {
                sessionStatus.setComplete();
                throw new InvalidRequestException("Cannot approve uninitialized authorization request.");
            } else {
                RedirectView var8;
                try {
                    Set responseTypes = authorizationRequest.getResponseTypes();
                    authorizationRequest.setApprovalParameters(approvalParameters);
                    authorizationRequest = this.userApprovalHandler.updateAfterApproval(authorizationRequest, (Authentication)principal);
                    boolean approved = this.userApprovalHandler.isApproved(authorizationRequest, (Authentication)principal);
                    authorizationRequest.setApproved(approved);
                    if (authorizationRequest.getRedirectUri() == null) {
                        sessionStatus.setComplete();
                        throw new InvalidRequestException("Cannot approve request when no redirect URI is provided.");
                    }

                    if (authorizationRequest.isApproved()) {
                        View var12;
                        if (responseTypes.contains("token")) {
                            var12 = this.getImplicitGrantResponse(authorizationRequest).getView();
                            return var12;
                        }

                        var12 = this.getAuthorizationCodeResponse(authorizationRequest, (Authentication)principal);
                        return var12;
                    }

                    var8 = new RedirectView(this.getUnsuccessfulRedirect(authorizationRequest, new UserDeniedAuthorizationException("User denied access"), responseTypes.contains("token")), false, true, false);
                } finally {
                    sessionStatus.setComplete();
                }

                return var8;
            }
        }
    }

    private ModelAndView getUserApprovalPageResponse(Map model, AuthorizationRequest authorizationRequest, Authentication principal) {
        this.logger.debug("Loading user approval page: " + this.userApprovalPage);
        model.putAll(this.userApprovalHandler.getUserApprovalRequest(authorizationRequest, principal));
        return new ModelAndView(this.userApprovalPage, model);
    }

    private ModelAndView getImplicitGrantResponse(AuthorizationRequest authorizationRequest) {
        try {
            TokenRequest tokenRequest = this.getOAuth2RequestFactory().createTokenRequest(authorizationRequest, "implicit");
            OAuth2Request storedOAuth2Request = this.getOAuth2RequestFactory().createOAuth2Request(authorizationRequest);
            OAuth2AccessToken accessToken = this.getAccessTokenForImplicitGrant(tokenRequest, storedOAuth2Request);
            if (accessToken == null) {
                throw new UnsupportedResponseTypeException("Unsupported response type: token");
            } else {
                return new ModelAndView(new RedirectView(this.appendAccessToken(authorizationRequest, accessToken), false, true, false));
            }
        } catch (OAuth2Exception var5) {
            return new ModelAndView(new RedirectView(this.getUnsuccessfulRedirect(authorizationRequest, var5, true), false, true, false));
        }
    }

 //..........省略相关代码

    private View getAuthorizationCodeResponse(AuthorizationRequest authorizationRequest, Authentication authUser) {
        try {
            return new RedirectView(this.getSuccessfulRedirect(authorizationRequest, this.generateCode(authorizationRequest, authUser)), false, true, false);
        } catch (OAuth2Exception var4) {
            return new RedirectView(this.getUnsuccessfulRedirect(authorizationRequest, var4, false), false, true, false);
        }
    }
 //..........省略相关代码


    private ModelAndView handleException(Exception e, ServletWebRequest webRequest) throws Exception {
        ResponseEntity translate = this.getExceptionTranslator().translate(e);
        webRequest.getResponse().setStatus(translate.getStatusCode().value());
        if (!(e instanceof ClientAuthenticationException) && !(e instanceof RedirectMismatchException)) {
            AuthorizationRequest authorizationRequest = null;

            try {
                authorizationRequest = this.getAuthorizationRequestForError(webRequest);
                String requestedRedirectParam = (String)authorizationRequest.getRequestParameters().get("redirect_uri");
                String requestedRedirect = this.redirectResolver.resolveRedirect(requestedRedirectParam, this.getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId()));
                authorizationRequest.setRedirectUri(requestedRedirect);
                String redirect = this.getUnsuccessfulRedirect(authorizationRequest, (OAuth2Exception)translate.getBody(), authorizationRequest.getResponseTypes().contains("token"));
                return new ModelAndView(new RedirectView(redirect, false, true, false));
            } catch (OAuth2Exception var8) {
                return new ModelAndView(this.errorPage, Collections.singletonMap("error", translate.getBody()));
            }
        } else {
            return new ModelAndView(this.errorPage, Collections.singletonMap("error", translate.getBody()));
        }
    }

  //..........省略相关代码
}

Внутренне реализовано в исходном кодеModelAndViewЧтобы сделать прыжок, можно собственно настроить его отображение.Согласно этой идее, мы также можем реализоватьViewсделать прыжок

Процесс обработки

Мысль:Будь то обычный процесс входа в систему или процесс функции «Запомнить меня», переход непосредственно на страницуscopeа такжеuser_oauth_approvalПодождите, пока данные заполнятся, а затем отправьте их автоматически.
Во-первых, на стойке регистрации необходимо использовать отправку формы, иначе она не подействует. Это место меня надолго застряло. Если это ajax, то в функции ошибки будет возвращен код всей страницы, потому что возвращаемое представление — это то место, куда возвращается пользовательская страница авторизации.
Давайте посмотрим на конкретный код


@RequestMapping({ "/oauth/my_approval"})
    public String getAccessConfirmation(Map model, HttpServletRequest request) throws Exception {
        @SuppressWarnings("unchecked")
        Map scopes = (Map) (model.containsKey("scopes") ? model.get("scopes") : request.getAttribute("scopes"));
        List list = new ArrayList<>();
        for (String scope : scopes.keySet()) {
            list.add(scope);
        }
        model.put("scopes",list);
        Cookie[] cookies = request.getCookies();
        boolean bool =  Arrays.stream(cookies).anyMatch(x->x.getName().equals("remember-me-cookie-name"));
        Principal principal = request.getUserPrincipal();
        String usernmae = principal.getName();
        model.put("username",usernmae);
        String check = bool==true ? "true" : "false";
        model.put("remember",check);

        return "approval";
    }

Взгляните на мой конкретныйapproval.htmlПосмотреть

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8"/>
</head>
<body>
<form th:action="@{../oauth/authorize}" th:method="post">
    <div th:each="scope:${scopes}">
        <input th:type="hidden" th:name="${scope}" th:value="true"/>
    </div>
    <input th:type="hidden" th:name="user_oauth_approval" th:value="true"/>
</form>
<script src="../../js/jquery.min.js">
<script src="../../js/jquery.cookie.js">
<script th:inline="javascript">
    window.onload=function(){
        var username = [[${username}]];
        var remember = [[${remember}]];
        if(remember == "true"){
            $.cookie('phone', username, { expires:7,path: '/' });
        }else{
            $.cookie('phone', username, { path: '/' });
        }
        document.forms[0].submit();
    }
</script>
</body>
</html>

Обратите внимание на изменение файлов конфигурации, из-за ограничений синтаксиса тимелеафа вы можете запускать Baidu по определенным причинам, и я не буду здесь слишком много рассказывать. . . . . .
file
Когда вы доберетесь до этого места, многие люди могут подумать, что все кончено, но это еще не так.remember-me-cookie-name, когда вы снова войдете в систему, он снова выполнит процесс запоминания меня, что приведет к ошибке входа

Решить выход из системы

Сначала, конечно, я думал о очистке куки.Я пробовал это место в течение долгого времени без успеха, и был осмеян великим богом в нашей группе, который только говорил, но не практиковал (java это наука, и простые вещи встречаются разные ситуации. , это может быть нелегко лечить), я надеюсь, что некоторые великие боги смогут найти решение после прочтения этой статьиsping security oauth2Файл cookie, который запоминает мою функцию, и, наконец, рассказать о моем решении, которое похоже на предыдущую идею работы с токенами, Поскольку некоторая информация о пользователе сохраняется в файле cookie браузера, эти данные должны быть проверены с помощью данные, хранящиеся в базе данных. , так как файл cookie не может быть обработан, то центр передачи будет обрабатывать базу данных.
Взгляните на код для выхода и входа в систему


//....................
@FrameworkEndpoint
public class RevokeTokenEndpoint {

    @Autowired
    @Qualifier("consumerTokenServices")
    ConsumerTokenServices consumerTokenServices;

    private static final Logger logger = LoggerFactory.getLogger(RevokeTokenEndpoint.class);

    @DeleteMapping("/oauth/exit")
    @ResponseBody
    public JSONObject revokeToken(String principal) {
           //消除token
        String access_token = JdbcOperateUtils.query(principal);
        if (!access_token.equals("gzw")) {
            if (consumerTokenServices.revokeToken(access_token)) {
                logger.info("oauth2 logout success with principal: "+ principal);
                 //消除cookie的校验数据
                JdbcOperateUtils.exit(principal);
                return ResultUtil.toJSONString(ResultEnum.SUCCESS,principal);
            }
        }else {
            logger.info("oauth2 logout fail with principal: "+ principal);
            return ResultUtil.toJSONString(ResultEnum.FAIL,principal);
        }
        return ResultUtil.toJSONString(ResultEnum.UNKONW_ERROR,principal);
    }
}

Код проверки данных для удаления файлов cookie выглядит следующим образом:


 /**


 * 查询用户是否是用记住我登录
 * @param username
 * @return
 */
public static void exit(String username) {
    Connection connection = ConnectionUtils.getConn();
    String sql1 = "SELECT series FROM persistent_logins WHERE username = ? limit 1";
    String sql2 = "DELETE  FROM persistent_logins WHERE username = ? ";
    PreparedStatement preparedStatement1 = null;
    PreparedStatement preparedStatement2 = null;
    try {
        preparedStatement1 = connection.prepareStatement(sql1);
        preparedStatement1.setString(1, username);
        ResultSet resultSet = preparedStatement1.executeQuery();
        if (resultSet.next()){
            preparedStatement2 = connection.prepareStatement(sql2);
            preparedStatement2.setString(1, username);
            preparedStatement2.execute();
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        ConnectionUtils.releaseConnection(connection);
    }
}


                                
                                

                                

Визуализация базы данных
file
Наконец, прикрепите оператор создания таблицы
Создайтеpersistent_loginsповерхность
create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null);

Суммировать:

Я надеюсь, что эта статья может быть полезна вам в вашей повседневной работе, и я буду продолжать обновлять статью с соответствующими проблемами, возникающими в моей повседневной работе. В будущем я синхронизирую код с github, чтобы все могли учиться вместе, и я надеюсь, что каждый сможет высказать свое мнение.
Обратитесь к моему адресу github:GitHub.com/Exact Evidence/Судный день для вас…

Серия Spring Security Oauth2 (1)
Серия Spring Security Oauth2 (2)
Серия Spring Security Oauth2 (3)
Серия Spring Security Oauth2 (четыре)