java - 如何在 Spring Boot 中使用 JWT 身份验证实现基本身份验证?

标签 java spring-boot spring-security jwt basic-authentication

我已经构建了一个使用 jwt 身份验证的 Spring-Boot 应用程序。

<?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 安全的两个安全(表单基础,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;
    }

}

最佳答案

您必须使用不同的根 URL 创建两个不同的 WebSecurityConfigurerAdapter 配置。如果 URL 重叠(即 /admin 和/**),那么您将需要在配置中使用 @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 或安全上下文。因此,如果您从一个登录页面登录,然后尝试转到另一个 protected 资源,您将不会被重定向到下一个登录页面。相反,您将获得 403(取决于不同登录页面分配的角色)。一次只能维持一个登录 session 。

关于java - 如何在 Spring Boot 中使用 JWT 身份验证实现基本身份验证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44302457/

相关文章:

java - 进行一些更改后重建 JDK1.6.8

用于翻阅基于 JPane 的 View 的 Java Swing 架构

java - 在 Spring Boot 中动态加载 Spring Bean 和组件

spring - 自定义登录 + Spring Security + Tiles + 请求方法 'POST' 不支持

spring - @AuthenticationPrincipal 对象返回 session 值

Grails Spring Security Core 在创建新用户时登录用户

java - 定期检查超时条件

java - 连接 Spring-Boot 到 MySQL

java - 无法使用 spring boot 渲染 thymeleaf

java - 在 BaseAdapter 中填充数据时出现 NullPointerException