我正在尝试在Spring中创建REST API和Web/MVC应用程序。它们应该使用相同的服务层。我是否可以在Spring中使用两种完全不同的配置(API的令牌身份验证,web的cookies,web的404页面,等等)?或者我应该做两个独立的Spring应用程序?
Spring-MVC默认配置有助于
>
控制器可以返回ModelAndView用于Web应用程序视图服务。
Controller可以用作RestController
,默认情况下响应由HttpMessageConverters
处理,其中Controller方法用作Rest-API
但是,我们可以使用spring-security
,这是一个基于过滤器的框架,它在您的Rest API和使用Rest API的客户端应用程序之间充当
安全墙(HTTP防火墙)
或者在spring-mvc
应用程序和最终用户之间充当
安全墙(HTTP防火墙)
实现-类型1。只有当身份验证令牌存在且有效时,才应访问Rest API。
“/api/**”
此配置将不考虑URL的其余部分。此http配置将配置为无状态。您应该配置OncePerRequestFilter
(例如JWTAUTHFilter
)的实现,并且筛选顺序可以在UsernamePasswordAuthenticationFilter
或BasicAuthenticationFilter
之前。但您的筛选器应读取身份验证令牌的头,验证它,并创建身份验证
对象,并将其设置为SecurityContext
而不会失败。JWTauthFilter
,而是配置UsernamePasswordAuthenticationFilter
(.formlogin()
为您执行此操作)@Configuration
@EnableWebSecurity
@ComponentScan(basePackages = "com.gmail.nlpraveennl")
public class SpringSecurityConfig
{
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
@Configuration
@Order(1)
public static class RestApiSecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private JwtAuthenticationTokenFilter jwtauthFilter;
@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.csrf().disable()
.antMatcher("/api/**")
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/**").hasAnyRole("APIUSER")
.and()
.addFilterBefore(jwtauthFilter, UsernamePasswordAuthenticationFilter.class);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
@Configuration
@Order(2)
public static class LoginFormSecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
public void configureInMemoryAuthentication(AuthenticationManagerBuilder auth) throws Exception
{
auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder.encode("admin@123#")).roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.csrf().disable()
.antMatcher("/**").authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/**").hasRole("ADMIN")
.and().formLogin();
http.sessionManagement().maximumSessions(1).expiredUrl("/login?expired=true");
}
}
}
“/**”
UsernamePasswordAuthenticationFilter
和JWTauthFilter
配置了此http配置,但JWTauthFilter
应在UsernamePasswordAuthenticationFilter
之前配置。UsernamePasswordAuthenticationFilter
,如果SecurityContext
中没有有效的auth对象,则将调用UsernamePasswordAuthenticationFilter
的尝试验证方法。如果JWTauthFilter
验证令牌并将身份验证对象设置为SecurityContext
,则即使筛选器链到达UsernamePasswordAuthenticationFilter
尝试验证方法也不会被调用,因为SecurityContext
中已经设置了身份验证对象。@Configuration
@EnableWebSecurity
@ComponentScan(basePackages = "com.gmail.nlpraveennl")
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private JwtAuthenticationTokenFilter jwtauthFilter;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
public void configureInMemoryAuthentication(AuthenticationManagerBuilder auth) throws Exception
{
auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder.encode("admin@123#")).roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.csrf().disable()
.antMatcher("/**").authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/**").hasAnyRole("APIUSER","ADMIN")
.antMatchers("/**").hasRole("ADMIN")
.and()
.formLogin()
.and()
.addFilterBefore(jwtauthFilter, UsernamePasswordAuthenticationFilter.class);
http.sessionManagement().maximumSessions(1).expiredUrl("/login?expired=true");
}
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
}
这是关于两种类型的实现,您可以选择任何类型的实现取决于您的需求。对于这两种实现类型jwtAuthenticationTokenFilter
和jwtTokenUtil
都是通用的,如下所示。
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException
{
final String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer "))
{
String authToken = header.substring(7);
try
{
String username = jwtTokenUtil.getUsernameFromToken(authToken);
if (username != null)
{
if (jwtTokenUtil.validateToken(authToken, username))
{
// here username should be validated with database and get authorities from database if valid
// Say just to hard code
List<GrantedAuthority> authList = new ArrayList<>();
authList.add(new SimpleGrantedAuthority("ROLE_APIUSER"));
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, null, authList);
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
else
{
System.out.println("Token has been expired");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
}
}
catch (Exception e)
{
System.out.println("Unable to get JWT Token, possibly expired");
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
}
chain.doFilter(request, response);
}
}
@Component
public class JwtTokenUtil implements Serializable
{
private static final long serialVersionUID = 8544329907338151549L;
// public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60 * 1000; // 5 Hours
public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 1000; // 5 Minutes
private String secret = "my-secret";
public String getUsernameFromToken(String token)
{
return getClaimFromToken(token, Claims::getSubject);
}
public Date getExpirationDateFromToken(String token)
{
return getClaimFromToken(token, Claims::getExpiration);
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver)
{
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token)
{
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token)
{
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
public String generateToken(String username)
{
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, username);
}
private String doGenerateToken(Map<String, Object> claims, String subject)
{
return "Bearer "+Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY)).signWith(SignatureAlgorithm.HS512, secret).compact();
}
public Boolean validateToken(String token, String usernameFromToken)
{
final String username = getUsernameFromToken(token);
return (username.equals(usernameFromToken) && !isTokenExpired(token));
}
}
您可以从下面给出的我的github存储库链接下载工作示例。
实现类型-1
实现类型-2
如果你对Spring Security的执行顺序感到好奇,你可以在这里参考我的答案->Spring Security filter chain是如何工作的
如果在类路径上添加了Spring Boot Security依赖项,则Spring Boot应用程序会自动要求对所有HTTP端点进行基本身份验证。 端点“/”和“/ home”不需要任何身份验证。 所有其他端点都需要身份验证。 要将Spring Boot Security添加到Spring Boot应用程序,我们需要在构建配置文件中添加Spring Boot Starter Security依赖项
问题内容: 我可以在一个应用程序中包含多个web.xml文件吗?并且两个应用程序可以具有父子关系,以便有两个web.xml吗? 问题答案: 对于Servlet 3.0以下,您不能。 如果您使用的是3.0,则有可能。 在JSR 315:Java Servlet 3.0规范中,引入了 web-fragment.xml 来实现库jar的可插入性,这些库jar打包在 WEB-INF / lib下 。web
问题内容: 据我所知,我可以使用Golang运行简单的Web服务器,只需使用软件包即可,例如 其中PORT是要侦听的TCP地址。 我可以将PORT用作PORT S ,例如从一个应用程序中使用吗? 我的问题可能很愚蠢,但是“谁不问,他就不会回答!” 感谢前进! 问题答案: 你不能。 但是,您可以在不同的端口上启动多个侦听器
本文向大家介绍如何使用SpringSecurity保护程序安全,包括了如何使用SpringSecurity保护程序安全的使用技巧和注意事项,需要的朋友参考一下 首先,引入依赖: 引入此依赖之后,你的web程序将拥有以下功能: 所有请求路径都需要认证 不需要特定的角色和权限 没有登录页面,使用HTTP基本身份认证 只有一个用户,名称为user 配置SpringSecurity springsecur
我想为DropWizard创建几个yaml文件。其中一个包含敏感信息,另一个包含非敏感信息。 你能给我指出任何文档或例子如何在DropWizard中拥有多个配置吗?
本文向大家介绍基于Spring Boot保护Web应用程序,包括了基于Spring Boot保护Web应用程序的使用技巧和注意事项,需要的朋友参考一下 如果在类路径上添加了Spring Boot Security依赖项,则Spring Boot应用程序会自动为所有HTTP端点提供基本身份验证。端点“/”和“/home”不需要任何身份验证。所有其他端点都需要身份验证。 要将Spring Boot S