一个应用程序中的 Spring Security OAuth2 身份验证和表单登录

Posted

技术标签:

【中文标题】一个应用程序中的 Spring Security OAuth2 身份验证和表单登录【英文标题】:Spring security OAuth2 authentication and form login in one app 【发布时间】:2016-01-24 08:32:56 【问题描述】:

是否可以在一个应用程序中通过 login basic 和 oauth2 结合授权和身份验证?

我的项目基于 jhipster 项目,带有简单的 spring 安全会话登录,现在我需要为移动应用程序添加 oauth2 安全性,看起来这是不可能的。

现在我在工作其中之一时遇到了情况,如果 WebSecurityConfigurerAdapter 的订单号大于 ResourceServerConfiguration,oauth2 可以。这意味着如果 oauth 安全过滤器是第一个。 我在 *** 中阅读了很多内容并尝试了许多解决方案,例如: Spring security oauth2 and form login configuration 对我来说那是行不通的。 现在我知道这与一些安全过滤器冲突有关,但我不知道如何解决它。

如果有人遇到类似问题并且他设法解决了问题,或者知道如何解决或改进问题,我将不胜感激。提前感谢您的帮助:)

@Configuration
@EnableWebSecurity
public class SecurityOauth2Configuration extends WebSecurityConfigurerAdapter 

  @Inject
  private UserDetailsService userDetailsService;

  @Override
  @Bean
  public AuthenticationManager authenticationManagerBean() throws Exception 
    return super.authenticationManagerBean();

@Bean
public PasswordEncoder passwordEncoder() 
    return new BCryptPasswordEncoder();


@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
    auth
        .userDetailsService(userDetailsService)
        .passwordEncoder(passwordEncoder());


@Override
public void configure(WebSecurity web) throws Exception 
    web.ignoring()
        .antMatchers("/scripts/**/*.js,html")
        .antMatchers("/bower_components/**")
        .antMatchers("/i18n/**")
        .antMatchers("/assets/**")
        .antMatchers("/swagger-ui/index.html")
        .antMatchers("/api/register")
        .antMatchers("/api/activate")
        .antMatchers("/api/account/reset_password/init")
        .antMatchers("/api/account/reset_password/finish")
        .antMatchers("/test/**");



@Configuration
@EnableAuthorizationServer
public static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter 

    private static final String OAUTH_SECURITY = "jhipster.security.authentication.oauth.";
    private static final String CLIENTID = "clientid";
    private static final String SECRET = "secret";
    private static final String TOKEN_VALIDATION_TIME = "tokenValidityInSeconds";

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception 
        oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('"+AuthoritiesConstants.USER+"')").checkTokenAccess("hasAuthority('"+AuthoritiesConstants.USER+"')");
    
    @Inject
    private Environment env;
    @Inject
    private DataSource dataSource;

    @Bean
    public TokenStore tokenStore() 
        return new JdbcTokenStore(dataSource);
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
        endpoints
            .authenticationManager(authenticationManager)
            .tokenStore(tokenStore())
        ;
    




    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
        clients
            .inMemory()
            .withClient(env.getProperty(OAUTH_SECURITY + CLIENTID))
            .scopes("read", "write")
            .authorities(AuthoritiesConstants.ADMIN, AuthoritiesConstants.USER)
            .authorizedGrantTypes("password", "refresh_token")
            .secret(env.getProperty(OAUTH_SECURITY + SECRET))
            .accessTokenValiditySeconds(env.getProperty(OAUTH_SECURITY + TOKEN_VALIDATION_TIME, Integer.class, 18000));

    



@Configuration
@Order(1)
public static class SecurityWebConfiguration extends WebSecurityConfigurerAdapter 
    @Inject
    private Environment env;

    @Inject
    private AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler;

    @Inject
    private AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler;

    @Inject
    private AjaxLogoutOauthSuccessHandler ajaxLogoutSuccessHandler;

    @Inject
    private RememberMeServices rememberMeServices;


    @Override
    protected void configure(HttpSecurity http) throws Exception 


        http
                .csrf().disable().authorizeRequests()
            .and()
                .formLogin()
                .loginProcessingUrl("/api/authentication")
                .successHandler(ajaxAuthenticationSuccessHandler)
                .failureHandler(ajaxAuthenticationFailureHandler)
                .usernameParameter("j_username")
                .passwordParameter("j_password")
                .permitAll()
            .and()
                .rememberMe()
            .rememberMeServices(rememberMeServices)
                .key(env.getProperty("jhipster.security.rememberme.key"))
            .and()
                .logout()
            .logoutUrl("/api/logout")
            .logoutSuccessHandler(ajaxLogoutSuccessHandler)
            .deleteCookies("JSESSIONID")
                .permitAll()
            .and()
                .exceptionHandling()
        ;



    




@Order(2)
@Configuration
@EnableResourceServer
public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter 
    @Inject
    private Http401UnauthorizedEntryPoint authenticationEntryPoint;

    @Inject
    private AjaxLogoutOauthSuccessHandler ajaxLogoutSuccessHandler;

    @Override
    public void configure(HttpSecurity http) throws Exception 
        ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
        if (contentNegotiationStrategy == null) 
            contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
        
        MediaTypeRequestMatcher preferredMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
            MediaType.APPLICATION_FORM_URLENCODED,
            MediaType.APPLICATION_JSON,
            MediaType.MULTIPART_FORM_DATA);


        http
            .authorizeRequests()
            .and()
                .anonymous()
            .disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
                .httpBasic()
            .and()
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .defaultAuthenticationEntryPointFor(authenticationEntryPoint, preferredMatcher)
            .and()
                .authorizeRequests()
                .antMatchers("/api/**").fullyAuthenticated();


    



对于此设置,WebSecurityConfigurerAdapter 会话可以正常工作。对于正确授权后的 OAuth,我得到有效的访问令牌,但对于会话中使用此令牌的请求,我得到以下结果:

  public static String getCurrentLogin() 
    SecurityContext securityContext = SecurityContextHolder.getContext();
    Authentication authentication = securityContext.getAuthentication();


    UserDetails springSecurityUser = null;
    String userName = null;
    if(authentication != null) 
        if (authentication.getPrincipal() instanceof UserDetails) 
            springSecurityUser = (UserDetails) authentication.getPrincipal();
            userName = springSecurityUser.getUsername();
         else if (authentication.getPrincipal() instanceof String) 
            userName = (String) authentication.getPrincipal();
        
    
    System.out.println(userName);                          // show anonymousUser
    System.out.println(authentication.isAuthenticated());  //show true
    System.out.println(authentication.getAuthorities());   //show [ROLE_ANONYMOUS]
    System.out.println(userName);                          //show anonymousUser

    return userName;

在控制台中写入函数: 匿名用户 真的 [ROLE_ANONYMOUS] 匿名用户

并且应该是user1 真的 [ROLE_USER] 用户1

【问题讨论】:

您能否在 Github 或其他地方发布一个简单但功能齐全的演示应用程序来演示您的问题?谢谢 【参考方案1】:

应用程序的 git 网址: https://github.com/rynkowsw/oauth2 是 oauth2 应用 https://github.com/rynkowsw/web-and-oauth2-security 这是 web 和 oauth2 安全应用程序

本应用改编自 jhipster.github.io

要运行应用程序,您需要在 localhost 中有 postgres db,例如在 db 资源文件中:

    driver-class-name: org.postgresql.ds.PGSimpleDataSource
    url: jdbc:postgresql://localhost:5432/gymapp
    name: gymapp
    serverName: localhost:5432
    username: postgres
    password: jaja

测试应用最快的方法是:

 http://localhost:8080/oauth/token
 headers:  Authorization: Basic amhpcHN0ZXJhcHA6bXlTZWNyZXRPQXV0aFNlY3JldA==

basic后面的这个字符串是组合default jhispter oauth secret和clientid base64加密结果

然后

  http://localhost:8080/api/account
  headers:  Authorization: bearer [token from response in first request]

对于同一个数据库,oauth 的结果是: 对于 oauth2 应用程序


 login: "user"
 password: null
 firstName: "User"
 lastName: "User"
 email: "user@localhost"
 activated: true
 langKey: "en"
 authorities: [1]
 0:  "ROLE_USER"
 -

对于 web + oauth2 安全性:

 
  login: "anonymousUser"
  password: null
  firstName: "Anonymous"
  lastName: "User"
  email: "anonymous@localhost"
  activated: true
  langKey: "en"
  authorities: [0]
  

【讨论】:

你找到答案了吗?

以上是关于一个应用程序中的 Spring Security OAuth2 身份验证和表单登录的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security 会话中的纯文本密码

一个应用程序中的 Spring Security OAuth2 身份验证和表单登录

Tomcat 和 spring-security 中的 Web 应用程序和 REST 服务 SSO

为啥应用程序看不到 Spring Security 中的角色(禁止)

将 Facebook Authentication 集成到 FacebookApp 中的 Spring Security

旧版应用程序中的 Spring Security 实现