Làm cách nào để tải Đối tượng Phiên trong Spring?


88

Tôi tương đối mới với mùa xuân và mùa xuân an ninh.

Tôi đang cố gắng viết một chương trình mà tôi cần xác thực người dùng ở cuối máy chủ bằng bảo mật Spring,

Tôi đã nghĩ ra những điều sau:

public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider{
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
                    throws AuthenticationException
    {
        System.out.println("Method invoked : additionalAuthenticationChecks isAuthenticated ? :"+usernamePasswordAuthenticationToken.isAuthenticated());
    }

    @Override
    protected UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException 
    {
        System.out.println("Method invoked : retrieveUser");
        //so far so good, i can authenticate user here, and throw exception if not authenticated!!
        //THIS IS WHERE I WANT TO ACCESS SESSION OBJECT
    }
}

Ưu điểm của tôi là khi người dùng được xác thực, tôi cần đặt một thuộc tính như:

session.setAttribute("userObject", myUserObject);

myUserObject là một đối tượng của một số lớp mà tôi có thể truy cập trong toàn bộ mã máy chủ của mình qua nhiều yêu cầu của người dùng.

Câu trả lời:


147

Bạn của bạn ở đây là org.springframework.web.context.request.RequestContextHolder

// example usage
public static HttpSession session() {
    ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
    return attr.getRequest().getSession(true); // true == allow create
}

Điều này sẽ được điền bởi servlet điều phối mvc mùa xuân tiêu chuẩn, nhưng nếu bạn đang sử dụng khung web khác, bạn đã thêm org.springframework.web.filter.RequestContextFilterlàm bộ lọc trong của mình web.xmlđể quản lý chủ sở hữu.

CHỈNH SỬA : chỉ là một vấn đề phụ mà bạn đang thực sự cố gắng làm gì, tôi không chắc bạn nên truy cập HttpSessionvào retieveUserphương pháp của a UserDetailsService. Spring security sẽ đưa đối tượng UserDetails vào phiên làm việc cho bạn. Nó có thể được lấy ra bằng cách truy cập vào SecurityContextHolder:

public static UserDetails currentUserDetails(){
    SecurityContext securityContext = SecurityContextHolder.getContext();
    Authentication authentication = securityContext.getAuthentication();
    if (authentication != null) {
        Object principal = authentication.getPrincipal();
        return principal instanceof UserDetails ? (UserDetails) principal : null;
    }
    return null;
}

1
@ điểm đầu tiên: Tôi đã thử sử dụng RequestContextHolder, nó đã báo cho tôi lỗi: Không tìm thấy yêu cầu ràng buộc luồng: Bạn đang đề cập đến ... sử dụng RequestContextListener hoặc RequestContextFilter để hiển thị yêu cầu hiện tại. Tôi đã không thử các bộ lọc, tôi đoán, sẽ thử và cho bạn biết nếu nó hoạt động. @second point: thực sự nó là một đối tượng tùy chỉnh, do đó tôi không thích UserDetails nếu có thể xem câu hỏi khác của tôi về chủ đề tương tự: stackoverflow.com/questions/1629273/…
Salvin Francis

2
nếu bạn không sử dụng các servlet văn bạn cần phải cấu hình: RequestContextFilter
Gareth Davis

1
xin lỗi sai lầm của tôi ... thích nghi mã từ dự án của riêng tôi trong đó sử dụng getRequest, câu trả lời sẽ được cập nhật
Gareth Davis

1
nope rằng vẻ bề ngoài về đúng ... cố gắng ngăn chặn mã của bạn trong một trình gỡ lỗi và kiểm tra rằng RequestContextFilter là trong các cuộc gọi stack
Gareth Davis

1
Tôi nhận thấy rằng RequestContextFilter không hoạt động với tôi trong khi RequestContextListener đã làm được ...<listener><listener-class>org.springframework.web.context.request.RequestContextListener</listener-class></listener>
Josh

33

Vì bạn đang sử dụng Spring, hãy gắn bó với Spring, đừng tự hack nó như những bài đăng khác.

Các nhãn hiệu mùa xuân nói:

Bạn không nên tương tác trực tiếp với HttpSession vì mục đích bảo mật. Đơn giản là không có lý do gì để làm như vậy - hãy luôn sử dụng SecurityContextHolder để thay thế.

Phương pháp hay nhất được đề xuất để truy cập phiên là:

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (principal instanceof UserDetails) {
  String username = ((UserDetails)principal).getUsername();
} else {
  String username = principal.toString();
}

Chìa khóa ở đây là Spring và Spring Security thực hiện tất cả những thứ tuyệt vời cho bạn như Ngăn chặn cố định phiên. Những điều này giả định rằng bạn đang sử dụng Spring framework vì nó được thiết kế để sử dụng. Vì vậy, trong servlet của bạn, hãy làm cho nó nhận biết ngữ cảnh và truy cập phiên như ví dụ trên.

Nếu bạn chỉ cần lưu trữ một số dữ liệu trong phạm vi phiên, hãy thử tạo một số bean phạm vi phiên như ví dụ này và để autowire thực hiện điều kỳ diệu của nó. :)


5

tôi đã tự tạo ra những lời nói của riêng mình. nó là tiện dụng. :)

package samples.utils;

import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.MessageSource;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.ui.context.Theme;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.ThemeResolver;
import org.springframework.web.servlet.support.RequestContextUtils;


/**
 * SpringMVC通用工具
 * 
 * @author 应卓(yingzhor@gmail.com)
 *
 */
public final class WebContextHolder {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebContextHolder.class);

    private static WebContextHolder INSTANCE = new WebContextHolder();

    public WebContextHolder get() {
        return INSTANCE;
    }

    private WebContextHolder() {
        super();
    }

    // --------------------------------------------------------------------------------------------------------------

    public HttpServletRequest getRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        return attributes.getRequest();
    }

    public HttpSession getSession() {
        return getSession(true);
    }

    public HttpSession getSession(boolean create) {
        return getRequest().getSession(create);
    }

    public String getSessionId() {
        return getSession().getId();
    }

    public ServletContext getServletContext() {
        return getSession().getServletContext();    // servlet2.3
    }

    public Locale getLocale() {
        return RequestContextUtils.getLocale(getRequest());
    }

    public Theme getTheme() {
        return RequestContextUtils.getTheme(getRequest());
    }

    public ApplicationContext getApplicationContext() {
        return WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    }

    public ApplicationEventPublisher getApplicationEventPublisher() {
        return (ApplicationEventPublisher) getApplicationContext();
    }

    public LocaleResolver getLocaleResolver() {
        return RequestContextUtils.getLocaleResolver(getRequest());
    }

    public ThemeResolver getThemeResolver() {
        return RequestContextUtils.getThemeResolver(getRequest());
    }

    public ResourceLoader getResourceLoader() {
        return (ResourceLoader) getApplicationContext();
    }

    public ResourcePatternResolver getResourcePatternResolver() {
        return (ResourcePatternResolver) getApplicationContext();
    }

    public MessageSource getMessageSource() {
        return (MessageSource) getApplicationContext();
    }

    public ConversionService getConversionService() {
        return getBeanFromApplicationContext(ConversionService.class);
    }

    public DataSource getDataSource() {
        return getBeanFromApplicationContext(DataSource.class);
    }

    public Collection<String> getActiveProfiles() {
        return Arrays.asList(getApplicationContext().getEnvironment().getActiveProfiles());
    }

    public ClassLoader getBeanClassLoader() {
        return ClassUtils.getDefaultClassLoader();
    }

    private <T> T getBeanFromApplicationContext(Class<T> requiredType) {
        try {
            return getApplicationContext().getBean(requiredType);
        } catch (NoUniqueBeanDefinitionException e) {
            LOGGER.error(e.getMessage(), e);
            throw e;
        } catch (NoSuchBeanDefinitionException e) {
            LOGGER.warn(e.getMessage());
            return null;
        }
    }

}

4

Thật vậy, bạn có thể truy cập thông tin từ phiên ngay cả khi phiên đang bị hủy trên HttpSessionLisener bằng cách:

public void sessionDestroyed(HttpSessionEvent hse) {
    SecurityContextImpl sci = (SecurityContextImpl) hse.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
    // be sure to check is not null since for users who just get into the home page but never get authenticated it will be
    if (sci != null) {
        UserDetails cud = (UserDetails) sci.getAuthentication().getPrincipal();
        // do whatever you need here with the UserDetails
    }
 }

hoặc bạn cũng có thể truy cập thông tin ở bất kỳ đâu bạn có sẵn đối tượng HttpSession như:

SecurityContextImpl sci = (SecurityContextImpl) session().getAttribute("SPRING_SECURITY_CONTEXT");

cuối cùng giả sử bạn có một cái gì đó như:

HttpSession sesssion = ...; // can come from request.getSession(false);

3

Tôi thử với mã tiếp theo và làm việc xuất sắc

    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.ModelMap;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;

    /**
     * Created by jaime on 14/01/15.
     */

    @Controller
    public class obteinUserSession {
        @RequestMapping(value = "/loginds", method = RequestMethod.GET)
        public String UserSession(ModelMap modelMap) {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            String name = auth.getName();
            modelMap.addAttribute("username", name);
            return "hellos " + name;
        }

4
Vui lòng thêm giải thích với mã của bạn. Xung quanh đây chỉ ném mã hoặc liên kết vào một câu hỏi nào đó không thực sự là câu trả lời.
Rémi

1

Nếu tất cả những gì bạn cần là thông tin chi tiết về Người dùng, đối với Phiên bản Spring 4.x, bạn có thể sử dụng @AuthenticationPrincipal@EnableWebSecuritygắn thẻ do Spring cung cấp như hình dưới đây.

Lớp cấu hình bảo mật:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   ...
}

Phương pháp điều khiển:

@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal User user) {
    ...
}

1

Trong trường hợp của tôi, tôi đã chèn HttpSession vào lớp CustomAuthenticationProvider như thế này

public class CustomAuthenticationProvider extends  AbstractUserDetailsAuthenticationProvider{

    @Autowired 
    private HttpSession httpSession;

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
             throws AuthenticationException
    {
        System.out.println("Method invoked : additionalAuthenticationChecks isAuthenticated ? :"+usernamePasswordAuthenticationToken.isAuthenticated());
    }

    @Override
    protected UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException 
    {
        System.out.println("Method invoked : retrieveUser");
        //so far so good, i can authenticate user here, and throw exception 
if not authenticated!!
        //THIS IS WHERE I WANT TO ACCESS SESSION OBJECT
        httpSession.setAttribute("userObject", myUserObject);
    }
}

0
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
attr.getSessionId();

5
Bạn có thể vui lòng thêm một số thông tin bổ sung vào câu trả lời của mình về cách giải quyết vấn đề này so với các câu trả lời hiện có khác không?
slfan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.