2017-06-01 119 views
9

我已經構建了一個Spring-Boot應用程序,它與jwt驗證一起使用。如何在Spring Boot中使用JWT身份驗證實現基本身份驗證?

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 

    <groupId>com.diplie</groupId> 
    <artifactId>rest-api</artifactId> 
    <version>1.0.0</version> 
    <packaging>war</packaging> 

    <parent> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-parent</artifactId> 
     <version>1.3.0.RC1</version> 
    </parent> 

    <properties> 
     <springfox-version>2.2.2</springfox-version> 
     <java.version>1.8</java.version> 
     <maven.test.skip>true</maven.test.skip> 
    </properties> 

    <dependencies> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-web</artifactId> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-test</artifactId> 
      <scope>test</scope> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-devtools</artifactId> 
      <optional>true</optional> 
     </dependency> 
     <dependency> 
      <groupId>com.fasterxml.jackson.core</groupId> 
      <artifactId>jackson-databind</artifactId> 
     </dependency> 
     <dependency> 
      <groupId>commons-beanutils</groupId> 
      <artifactId>commons-beanutils</artifactId> 
     </dependency> 
     <dependency> 
      <groupId>com.fasterxml.jackson.dataformat</groupId> 
      <artifactId>jackson-dataformat-xml</artifactId> 
     </dependency> 

     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-security</artifactId> 
     </dependency> 

     <dependency> 
      <groupId>io.jsonwebtoken</groupId> 
      <artifactId>jjwt</artifactId> 
      <version>LATEST</version> 
     </dependency> 

     <!-- Swagger 2 --> 
     <dependency> 
      <groupId>io.springfox</groupId> 
      <artifactId>springfox-swagger2</artifactId> 
      <version>${springfox-version}</version> 
     </dependency> 
     <dependency> 
      <groupId>io.springfox</groupId> 
      <artifactId>springfox-swagger-ui</artifactId> 
      <version>${springfox-version}</version> 
     </dependency> 
    </dependencies> 

    <dependencyManagement> 
     <dependencies> 
      <dependency> 
       <groupId>org.springframework.boot</groupId> 
       <artifactId>spring-boot-starter-tomcat</artifactId> 
       <version>${project.parent.version}</version> 
       <scope>provided</scope> 
      </dependency> 
     </dependencies> 
    </dependencyManagement> 

    <build> 
     <plugins> 
      <plugin> 
       <groupId>org.springframework.boot</groupId> 
       <artifactId>spring-boot-maven-plugin</artifactId> 
      </plugin> 
     </plugins> 
    </build> 

    <repositories> 
     <repository> 
      <id>repository.springsource.milestone</id> 
      <name>SpringSource Milestone Repository</name> 
      <url>http://repo.springsource.org/milestone</url> 
     </repository> 
    </repositories> 

    <pluginRepositories> 
     <pluginRepository> 
      <id>repository.springsource.milestone</id> 
      <name>SpringSource Milestone Repository</name> 
      <url>http://repo.springsource.org/milestone</url> 
     </pluginRepository> 
    </pluginRepositories> 

</project> 

我想有一個基本的身份驗證,當我使用Swagger我想有難同當我在Try Out按鈕

點擊例如彈出窗口:

enter image description here

如何在同一端點上使用spring安全的兩個安全(form base,JWT token)過濾器?

WebSecurityConfig

@Configuration 
@EnableWebSecurity 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 

     http.exceptionHandling().and().anonymous().and().servletApi().and().authorizeRequests() 

       // Allow anonymous resource requests 
       .antMatchers("/swagger-ui.html").permitAll().antMatchers("/").permitAll() 
       .antMatchers("/webjars/springfox-swagger-ui/**").permitAll().antMatchers("/swagger-resources/**") 
       .permitAll().antMatchers("/v2/api-docs").permitAll().antMatchers("/favicon.ico").permitAll() 
       .antMatchers("**/*.html").permitAll().antMatchers("**/*.css").permitAll().antMatchers("**/*.js") 
       .permitAll() 

       // Allow anonymous logins 
       .antMatchers("/user/User").permitAll().antMatchers("/locality/**").hasAuthority("Admin") 
       .antMatchers("/category/**").hasAuthority("Admin").antMatchers("/item").hasAuthority("Item") 
       .antMatchers("/item/userItems").hasAuthority("Item").antMatchers("item/lookFor").permitAll() 
       .antMatchers("item/items").hasAuthority("User") 

       // All other request need to be authenticated 
       .anyRequest().authenticated().and() 
       // And filter other requests to check the presence of JWT in 
       // header 
       .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); 
    } 

    @Override 
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 

     // Créer un compte par défaut 
     auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN"); 
    } 
} 

TokenAuthenticationService

public class TokenAuthenticationService { 

    static ResourceBundle bundle = ResourceBundle.getBundle("application"); 

    static void addAuthentication(HttpServletResponse res, String username) { 

     String JWT = Jwts.builder().setSubject(username) 
       .setExpiration(new Date(System.currentTimeMillis() + getExpirationTime())) 
       .signWith(SignatureAlgorithm.HS512, getSecret()).compact(); 
     res.addHeader(getHeaderString(), getTokenPrefix() + " " + JWT); 
    } 

    static Authentication getAuthentication(HttpServletRequest request) { 
     String token = request.getHeader(getHeaderString()); 
     if (token != null) { 
      // Analyse du jeton. 
      String user = Jwts.parser().setSigningKey(getSecret()).parseClaimsJws(token.replace(getTokenPrefix(), "")) 
        .getBody().getSubject(); 
      return user != null ? new UsernamePasswordAuthenticationToken(user, null, emptyList()) : null; 
     } 
     return null; 
    } 

    /** 
    * @return the secret 
    */ 
    public static String getSecret() { 
     return bundle.getString("secret"); 
    } 

    /** 
    * @return the expirationTime 
    */ 
    public static long getExpirationTime() { 
     return Long.valueOf(bundle.getString("expiration.time")); 
    } 

    /** 
    * @return the tokenPrefix 
    */ 
    public static String getTokenPrefix() { 
     return bundle.getString("token.prefix"); 
    } 

    /** 
    * @return the headerString 
    */ 
    public static String getHeaderString() { 
     return bundle.getString("header.string"); 
    } 

} 

JWTLoginFilter

public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter { 

    public JWTLoginFilter(String url, AuthenticationManager authManager) { 
     super(new AntPathRequestMatcher(url)); 
     setAuthenticationManager(authManager); 
    } 

    @Override 
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) 
      throws AuthenticationException, IOException, ServletException { 
     AccountCredentials creds = new ObjectMapper().readValue(req.getInputStream(), AccountCredentials.class); 
     return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(creds.getUsername(), 
       creds.getPassword(), Collections.emptyList())); 
    } 

    @Override 
    protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, 
      Authentication auth) throws IOException, ServletException { 
     TokenAuthenticationService.addAuthentication(res, auth.getName()); 
    } 
} 

JWTAuthenticationFilter

public class JWTAuthenticationFilter extends GenericFilterBean { 
    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 
      throws IOException, ServletException { 
     Authentication authentication = TokenAuthenticationService.getAuthentication((HttpServletRequest) request); 

     SecurityContextHolder.getContext().setAuthentication(authentication); 
     filterChain.doFilter(request, response); 
    } 
} 

AccountCredentials

public class AccountCredentials { 

    private String username; 
    private String password; 

    /** 
    * 
    */ 
    public AccountCredentials() { 
     super(); 
    } 

    /** 
    * @return the username 
    */ 
    public String getUsername() { 
     return username; 
    } 

    /** 
    * @param username 
    *   the username to set 
    */ 
    public void setUsername(String username) { 
     this.username = username; 
    } 

    /** 
    * @return the password 
    */ 
    public String getPassword() { 
     return password; 
    } 

    /** 
    * @param password 
    *   the password to set 
    */ 
    public void setPassword(String password) { 
     this.password = password; 
    } 

} 
+1

您目前的認證方案是什麼?你在哪裏存儲你的用戶信息? –

+0

@MarcTarin其實我沒有儲存任何東西。 – Mercer

回答

6

您必須創建兩個不同WebSecurityConfigurerAdapter配置與不同的根的URL。如果URL重疊(即/admin and /**),則需要在配置上使用@Order批註來定義優先級。

下面是HTTP基本和基於表單的身份驗證的工作示例。

https://github.com/ConsciousObserver/TestMultipleLoginPagesFormAndBasic.git

package com.test; 

import javax.servlet.http.HttpSession; 

import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.core.annotation.Order; 
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 

@SpringBootApplication 
public class TestMultipleLoginPagesApplication { 

    public static void main(String[] args) { 
     SpringApplication.run(TestMultipleLoginPagesApplication.class, args); 
    } 
} 

@Controller 
class MvcController { 
    @RequestMapping(path="form/formLogin", method=RequestMethod.GET) 
    public String formLoginPage() { 
     return "formLogin"; 
    } 

    @RequestMapping(path="form/formHome", method=RequestMethod.GET) 
    public String formHomePage() { 
     return "formHome"; 
    } 

    @RequestMapping(path="basic/basicHome", method=RequestMethod.GET) 
    public String userHomePage() { 
     return "basicHome"; 
    } 

    @RequestMapping(path="basic/logout", method=RequestMethod.GET) 
    public String userLogout(HttpSession session) { 
     session.invalidate(); 
     return "basicLogout"; 
    } 
} 

@Configuration 
@Order(1) 
class FormSecurity extends WebSecurityConfigurerAdapter { 
    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http.antMatcher("/form/**") 
      .authorizeRequests() 
       .anyRequest().hasRole("FORM_USER") 
      .and() 
      .formLogin() 
       .loginPage("/form/formLogin").permitAll() 
       .loginProcessingUrl("/form/formLoginPost").permitAll() 
       .defaultSuccessUrl("/form/formHome") 
      .and() 
       .logout().logoutUrl("/form/logout").logoutSuccessUrl("/form/formLogin") 
      .and() 
      .httpBasic().disable() 
      .csrf().disable(); 
    } 

    @Override 
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
     auth.inMemoryAuthentication() 
      .withUser("user") 
      .password("test") 
      .roles("FORM_USER"); 
    } 
} 

@Configuration 
@Order(2) 
class BasicAuthSecurity extends WebSecurityConfigurerAdapter { 
    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http.antMatcher("/basic/**") 
      .authorizeRequests() 
      .anyRequest().hasRole("BASIC_USER") 
      .antMatchers("/basic/logout").permitAll() 
      .and() 
       .httpBasic() 
      .and() 
       .csrf().disable(); 
    } 

    @Override 
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
     auth.inMemoryAuthentication() 
      .withUser("basic_user") 
      .password("test") 
      .roles("BASIC_USER"); 
    } 
} 

@Configuration 
@Order(3) 
class RootUrlSecurity extends WebSecurityConfigurerAdapter { 
    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     /* 
     * Put any security expectations from the root URL here, currently everything is permitted. 
     * Since it's the last in the order /form/** and /basic/** have a priority over it. 
     */ 
     http.antMatcher("/**") 
      .authorizeRequests() 
       .anyRequest().permitAll(); 
    } 
} 

注:由於這些登錄頁面不是來自不同的應用程序,它們共享SecurityContextHolder中或安全上下文。因此,如果您從一個登錄頁面登錄,然後嘗試使用另一個登錄頁面的受保護資源,則不會將您重定向到下一個登錄頁面。相反,你會得到403(取決於不同的登錄頁面分配的角色)。一次只能維護一次登錄會話。