我构建了一个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
按钮时有一个弹出窗口
例如:
如何在同一端点上使用弹簧安全性的两个安全性(表单库,JWT令牌)过滤器?
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;
}
}
答案 0 :(得分:6)
您必须使用不同的根URL创建两个不同的WebSecurityConfigurerAdapter
配置。如果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(取决于不同登录页面分配的角色)。一次只能维护一个登录会话。