当我点击'/ login'时,我想重新放置JWT。但是我错过了一些东西,无法解决。
以下是我的代码:
SecurytiApplication.java
-XX:ErrorFile
SecurityConfig.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories
@ComponentScan(basePackages = "com.example.securyti")
@EntityScan(basePackages = "com.example.securyti")
@SpringBootApplication
public class SecurytiApplication {
public static void main(String[] args) {
SpringApplication.run(SecurytiApplication.class, args);
}
}
JwtAuthenticationFilter.java
package com.example.securyti.config;
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.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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.example.securyti.security.UserAccountService;
import com.example.securyti.security.jwt.JwtAuthenticationFilter;
import com.example.securyti.security.jwt.JwtAuthorizationFilter;
import com.example.securyti.security.jwt.JwtTokenService;
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
protected JwtAuthenticationFilter jwtAuthenticationFilter;
protected JwtAuthorizationFilter JwtAuthorizationFilter;
@Autowired
protected UserAccountService userAccountService;
@Autowired
protected JwtTokenService jwtTokenService;
@Autowired
protected ConfigurationService configService;
@Override
protected void configure(HttpSecurity http) throws Exception {
final AuthenticationManager authenticationManager = authenticationManager();
jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager,
jwtTokenService, (BCryptPasswordEncoder) passwordEncoder(), userAccountService);
JwtAuthorizationFilter = new JwtAuthorizationFilter(authenticationManager,
configService, jwtTokenService);
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/register")
.permitAll()
.anyRequest().authenticated().and().addFilter(jwtAuthenticationFilter).addFilter(JwtAuthorizationFilter);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(final AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userAccountService).passwordEncoder(passwordEncoder());
}
}
JwtAuthorizationFilter.java
package com.example.securyti.security.jwt;
import java.io.IOException;
import java.util.Collections;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.StringUtils;
import com.example.securyti.security.UserAccount;
import com.example.securyti.security.UserAccountService;
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
private final AuthenticationManager authenticationManager;
private final JwtTokenService jwtTokenService;
private final BCryptPasswordEncoder passwordEncoder;
private final UserAccountService userAccountService;
public JwtAuthenticationFilter(
final AuthenticationManager authenticationManager,
final JwtTokenService jwtTokenService,
final BCryptPasswordEncoder passwordEncoder,
final UserAccountService userAccountService) {
this.authenticationManager = authenticationManager;
this.jwtTokenService = jwtTokenService;
this.passwordEncoder = passwordEncoder;
this.userAccountService = userAccountService;
}
@Override
public Authentication attemptAuthentication(final HttpServletRequest req,
final HttpServletResponse res) {
String jwt = jwtTokenService.getTokenFromRequest(req);
UserAccount userAccount = null;
if (StringUtils.hasText(jwt) && jwtTokenService.validateToken(jwt)) {
userAccount = (UserAccount) userAccountService.loadUserByUsername(jwtTokenService.getUsernameFromJWT(jwt));
}
if(userAccount == null){
throw new BadCredentialsException("Bad credentials");
}
AbstractAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userAccount.getUsername(),
userAccount.getPassword(), Collections.emptyList());
Authentication auth = authenticationManager.authenticate(authToken);
return auth;
}
private String getUsername(final UserAccount creds) {
if (creds != null) {
return creds.getUsername();
}
return null;
}
@Override
protected void successfulAuthentication(final HttpServletRequest req,
final HttpServletResponse res, final FilterChain chain,
final Authentication auth) throws IOException, ServletException {
final UserAccount account = (UserAccount) auth.getPrincipal();
jwtTokenService.addTokenToResponse(account, res);
super.successfulAuthentication(req, res, chain, auth);
}
}
JwtTokenService.java (这只是帮助程序类)
package com.example.securyti.security.jwt;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import com.example.securyti.config.ConfigurationService;
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
private ConfigurationService configService;
private JwtTokenService jwtTokenService;
public JwtAuthorizationFilter(AuthenticationManager authManager, ConfigurationService configService,
final JwtTokenService jwtTokenService) {
super(authManager);
this.configService = configService;
this.jwtTokenService = jwtTokenService;
}
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(configService.getHeaderField());
if (header == null || !header.startsWith(configService.getTokenPrefix())) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(configService.getHeaderField());
if (token != null) {
// parse the token.
String user = jwtTokenService.getUsernameFromJWT(token);
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
application.properties
package com.example.securyti.security.jwt;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.example.securyti.config.ConfigurationService;
import com.example.securyti.security.UserAccount;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
@Service
public class JwtTokenService {
private static final Logger logger = LoggerFactory.getLogger(JwtTokenService.class);
private ConfigurationService configurationService;
public JwtTokenService(final ConfigurationService configurationService) {
super();
this.configurationService = configurationService;
}
String getTokenFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader(configurationService.getHeaderField());
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(configurationService.getTokenPrefix())) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
public void addTokenToResponse(UserAccount account, HttpServletResponse res) {
LocalDateTime expiry = LocalDateTime.now().plusSeconds(configurationService.getJwtExpirationInSec());
String token = Jwts.builder()
.setSubject(account.getUsername())
.setIssuedAt(new Date())
.setExpiration(Date.from(expiry.atZone(ZoneId.systemDefault()).toInstant()))
.signWith(SignatureAlgorithm.HS512, configurationService.getJwtSecret())
.compact();
res.addHeader(configurationService.getHeaderField(), configurationService.getTokenPrefix() + token);
}
public String getUsernameFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(configurationService.getJwtSecret())
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(configurationService.getJwtSecret()).parseClaimsJws(authToken);
return true;
} catch (SignatureException ex) {
logger.error("Invalid JWT signature");
} catch (MalformedJwtException ex) {
logger.error("Invalid JWT token");
} catch (ExpiredJwtException ex) {
logger.error("Expired JWT token");
} catch (UnsupportedJwtException ex) {
logger.error("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
logger.error("JWT claims string is empty.");
}
return false;
}
}
控制器中没有处理程序方法“ \ login”。当前,当我使用有效的用户名和密码点击“ / login”时,在控制台上收到403和以下消息:
spring.datasource.url= jdbc:mysql://localhost:3306/mydb
spring.datasource.username= root
spring.datasource.password= root
spring.jpa.hibernate.ddl-auto = update
#TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
logging.level.root=DEBUG
## JWT
jwt.secret= JWTSuperSecretKey
jwt.expirationInSec = 10
jwt.tokenPrefix = Bearer
jwt.headerField = Authorization
我想念的是什么。如果我的理解在某处不对,请纠正我。预先感谢。
答案 0 :(得分:0)
可能使用带有JWT令牌的Spring Cloud安全性是更合适的选择。对于您的用例,您应该配置一个授权服务器,此工作在Spring Cloud Security中非常简单,该服务器将是如下所示的Spring Boot应用程序:
@Configuration
@EnableAuthorizationServer
public class SecurityOAuth2AutorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
private final PasswordEncoder passwordEncoder;
public SecurityOAuth2AutorizationServerConfig(AuthenticationManager authenticationManager,
PasswordEncoder passwordEncoder) {
this.authenticationManager = authenticationManager;
this.passwordEncoder = passwordEncoder;
}
@Bean
public UserDetailsService accountUserDetailsService() {
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
inMemoryUserDetailsManager.createUser(new User("user", passwordEncoder.encode("secret"),
Collections.singleton(new SimpleGrantedAuthority("USER"))));
return inMemoryUserDetailsManager;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.approvalStoreDisabled()
.authenticationManager(authenticationManager)
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
.userDetailsService(accountUserDetailsService)
.reuseRefreshTokens(false);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.tokenKeyAccess("permitAll()")
.passwordEncoder(passwordEncoder)
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.secret(passwordEncoder.encode("secret"))
.authorizedGrantTypes("authorization_code", "refresh_token", "password").scopes("openid")
.authorities("ROLE_USER", "ROLE_EMPLOYEE")
.scopes("read", "write", "trust", "openid")
.resourceIds("oauth2-resource")
.autoApprove(true)
.accessTokenValiditySeconds(5)
.refreshTokenValiditySeconds(60*60*8);
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
}
WebSecurityConfig类
@Configuration
@Order(SecurityProperties.DEFAULT_FILTER_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().httpBasic().disable()
.formLogin().loginPage("/login").loginProcessingUrl("/login")
.permitAll()
.and()
.requestMatchers().antMatchers("/account/userInfo", "/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests().anyRequest().authenticated();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
pom.xml
<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.example</groupId>
<artifactId>jwt-authserver</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>jwt-authserver</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7-1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
和如下登录页面:
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap.css}"/>
<link rel="stylesheet" th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap-theme.css}"/>
<title>Log In</title>
</head>
<body>
<div class="container">
<form role="form" action="login" method="post">
<div class="row">
<div class="form-group">
<div class="col-md-6 col-lg-6 col-md-offset-2 col-lg-offset-">
<label for="username">Username:</label>
<input type="text" class="form-control" id="username" name="username"/>
</div>
</div>
</div>
<div class="row">
<div class="form-group">
<div class="col-md-6 col-lg-6 col-md-offset-2 col-lg-offset-2">
<label for="password">Password:</label>
<input type="password" class="form-control" id="password" name="password"/>
</div>
</div>
</div>
<div class="row">
<div class="col-md-offset-2 col-lg-offset-2 col-lg-12">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
</div>
<script th:src="@{/webjars/jquery/3.2.0/jquery.min.js}"></script>
<script th:src="@{/webjars/bootstrap/3.3.7-1/js/bootstrap.js}" ></script>
</body>
</html>
通过这种方式,您拥有使用JWT令牌的完整授权服务器oauth2,即使看起来似乎付出了很大的努力,但我认为这是不使用自定义安全协议或路径的正确方法,因为它们很快就会成为现实难以维护且难以解决的安全问题,使用标准安全协议始终是最好的方法!
在客户端,您可以使用授权代码或密码oauth2标准流程自行实现Web sso,或者在客户端应用程序上使用具有以下简单配置的WebSSO功能在客户端应用程序上使用Spring Cloud Security:
@Configuration
@EnableOAuth2Sso
class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().and().httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.authorizeRequests().anyRequest().authenticated();
}
}
我希望它能为您提供帮助