Spring Security在我的春季启动项目中无法正常工作



我在spring启动应用程序中使用带有JWT令牌的spring安全性,并试图在jsp页面中使用与spring安全性相关的功能(如isauthenticated、hasRole、hasAuthority等(,但它们不起作用。在我的控制器中遇到了同样的问题,而在RESTController中,它们与@previuthorize标记一起工作得很好。

pom.xml:-

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.bdtool</groupId>
<artifactId>BD-Tool</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>BD-Tool</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.2</version>
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>4.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>

</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency> 
<groupId>javax.xml.bind</groupId> 
<artifactId>jaxb-api</artifactId> 
</dependency> 
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client</artifactId>
<version>1.31.5</version>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-gson</artifactId>
<version>1.22.0</version>
</dependency>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client-jackson2</artifactId>
<version>1.20.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

这是我的配置类:-

package com.bdtool.config;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource(name = "jwtUserDetailsService")
private UserDetailsService userDetailsService;
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests().antMatchers("/", "/index", "/resources/**", "/JS/**", "/register", "/authenticate", "/login",
"/signout", "/signup", "/forgotPassword", "/submitSignupDetails")
.permitAll().anyRequest().authenticated().and().exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public JwtRequestFilter authenticationTokenFilterBean() throws Exception {
return new JwtRequestFilter();
}
}

这是我的JwtRequestFilter


package com.bdtool.config;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import com.bdtool.service.JwtTokenBeanService;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.jsonwebtoken.ExpiredJwtException;

@Component
@JsonSerialize
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtUserDetailsService JwtUserDetailsService;

@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
JwtTokenBeanService jwtTokenBeanService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ") ) {
jwtToken = requestTokenHeader.substring(7);
try {

username = jwtTokenUtil.getUsernameFromToken(jwtToken);

System.out.println("From jwt req filter:-"+username);
} catch (IllegalArgumentException e) {
throw new ServletException("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
throw new ServletException("JWT Token has expired");
}


} else {
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.JwtUserDetailsService.loadUserByUsername(username);

if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);

}
}
chain.doFilter(request, response);
}
}

这是JwttokenUtil


package com.bdtool.config;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Component
public class JwtTokenUtil implements Serializable {

private static final long serialVersionUID = 1L;

public static final long JWT_TOKEN_VALIDITY = 60 * 60;

@Value("${jwt.secret}")
private String 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(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, userDetails.getUsername());
}
private String doGenerateToken(Map<String, Object> claims, String subject) {
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
.signWith(SignatureAlgorithm.HS512, secret).compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}

用户详细信息服务类:-

package com.bdtool.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.bdtool.bean.Users;
import com.bdtool.dao.UsersDao;

@Service
public class JwtUserDetailsService  implements UserDetailsService {


@Autowired
private UsersDao userdao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

Users user=userdao.findUserData(username);

if (user == null) {
throw new UsernameNotFoundException("User not found with username: " + username);
}

List<GrantedAuthority> authrole = new ArrayList<GrantedAuthority>() ;
authrole.add(new SimpleGrantedAuthority(user.getUserType()));
return new org.springframework.security.core.userdetails.User(username, user.getUserPassword(),
authrole);

}


}

AuthenticateController:-


package com.bdtool.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.bdtool.bean.Users;
import com.bdtool.config.JwtTokenUtil;
import com.bdtool.config.JwtUserDetailsService;
import com.bdtool.dao.UsersDao;
import com.bdtool.model.JwtRequest;
import com.bdtool.model.JwtResponse;
@RestController
@CrossOrigin
public class AuthenticateController {
@Autowired
private AuthenticationManager authenticationManager;

@Autowired
private JwtTokenUtil jwtTokenUtil;

@Autowired
private JwtUserDetailsService userDetailsService;



@PostMapping(value = "/authenticate")
public ResponseEntity<?> createAuthenticationToken(JwtRequest jwtRequest) {
try {


authenticate(jwtRequest.getUsername(), jwtRequest.getPassword());
final UserDetails userDetails = userDetailsService.loadUserByUsername(jwtRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));


}catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<>(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR);
}
}

private void authenticate(String username, String password) throws Exception {
try {

authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
e.printStackTrace();
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
e.printStackTrace();
throw new Exception("Invalid Credentials", e);
}
}
}

JwtAuthenticationEntryPoint:-


package com.bdtool.config;
import java.io.IOException;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
/**
* 
*/
private static final long serialVersionUID = 1L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}

}

AJAX->


$("#somebutton").click(function() {
jQuery.ajax({
url: 'logout',
type: 'GET',
headers: {
'Authorization': 'Bearer ' + sessionStorage.getItem("Token")
},
success: function() {
console.log("ok");
}
});
});

我的JSP代码:-

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@ taglib prefix="sec"
uri="http://www.springframework.org/security/tags"%>

class="menu-arrow"></span></a>
<ul>
<sec:authorize access="isAuthenticated()">
<li><a href="adminProjectDashboard">Project Dashboard</a></li>
<li><a href="adminProductDashboard">Product Dashboard</a></li>
</sec:authorize>
</ul></li>

这对我来说太复杂了。我在这里有一个从JPA回购中检索权限的示例资源服务器,web安全配置并不比更复杂

@EnableGlobalMethodSecurity(prePostEnabled = true)
public static class WebSecurityConfig {
@Bean
public ExpressionInterceptUrlRegistryPostProcessor expressionInterceptUrlRegistryPostProcessor() {
return (ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) -> registry
.antMatchers("/secured-route")
.hasRole("AUTHORIZED_PERSONNEL")
.anyRequest()
.authenticated();
}
@Bean
public SynchronizedJwt2AuthenticationConverter<JwtAuthenticationToken> authenticationConverter(
ClaimSet2AuthoritiesConverter<ClaimSet> authoritiesConverter) {
return jwt -> new JwtAuthenticationToken(jwt, authoritiesConverter.convert(new UnmodifiableClaimSet(jwt.getClaims())));
}
@Bean
public ClaimSet2AuthoritiesConverter<ClaimSet> authoritiesConverter(UserAuthorityRepository authoritiesRepo) {
return new PersistedGrantedAuthoritiesRetriever(authoritiesRepo);
}
}

它使用了一些由spring-boot自动配置的@ConditionalOnMissingBean,但您可以在源代码中找到列表(它们并不多(。

PS

一种更有效的方法是向JWT访问令牌(授权服务器内部(添加权限:SQL请求仅在发出新令牌时执行,而不是在每个请求时执行。

从JWT的私人索赔中很容易找到权威。

其他PS

您正在使用的Java版本非常不推荐使用,今年秋季合并的Spring版本将需要Java 17作为最低

最后一个PS

您的引导版本也不推荐使用,WebSecurityConfigurerAdapter不应该再使用,而应该提供SecurityFiterChain@Bean(这在您使用的引导版本中已经有效(

最新更新