通过使用spring框架和Angular 5构建应用程序,我感到很脏。
我使用弹簧靴,弹簧支架和弹簧安全装置准备好应用程序的后端。出于本申请的目的,我使用了弹簧JWT令牌进行授权。
我已经使用邮递员测试了后端服务,他们工作正常,没有任何故障。
现在,当我尝试将应用程序集成到前端(Angular 5)时,我一直面临CORS问题以及我无法访问前端角度应用程序发生的服务调用中的响应头。
我已经在WebSecurity文件中将CORS设置为Expose标头。
我能够在回复中看到在Chrome中带有授权令牌的令牌,但仍然无法访问。
任何帮助都将不胜感激。
谢谢!
任何人都可以建议在Spring启动应用程序中设置CORS的正确方法。我目前的申请如下所示。
User.java
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String username;
private String password;
public long getId() {
return id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
UserController.java
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.app.mycompany.TasksApp.model.User;
import com.app.mycompany.TasksApp.repository.UserRepository;
@RestController
@RequestMapping("/users")
public class UserController {
private UserRepository userRepository;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public UserController(UserRepository userRepository,
BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userRepository = userRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@PostMapping("/sign-up")
public void signUp(@RequestBody User user) {
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
userRepository.save(user);
}
}
UserRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import com.app.mycompany.TasksApp.model.User;
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
UserDetailsServiceImpl.java
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.app.mycompany.TasksApp.model.User;
import com.app.mycompany.TasksApp.repository.UserRepository;
import org.springframework.stereotype.Service;
import static java.util.Collections.emptyList;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository userRepository;
public UserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), emptyList());
}
}
WebSecurity.java
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import static com.app.mycompany.TasksApp.security.SecurityConstants.SIGN_UP_URL;
import java.util.Arrays;
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.and().cors().and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET","POST", "OPTIONS", "PUT", "DELETE"));
configuration.setAllowCredentials(true);
configuration.setExposedHeaders(Arrays.asList("Content-type","Authorization"));
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
}
JWTAuthenticationFilter.java
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import com.app.mycompany.TasksApp.model.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import static com.app.mycompany.TasksApp.security.SecurityConstants.EXPIRATION_TIME;
import static com.app.mycompany.TasksApp.security.SecurityConstants.HEADER_STRING;
import static com.app.mycompany.TasksApp.security.SecurityConstants.SECRET;
import static com.app.mycompany.TasksApp.security.SecurityConstants.TOKEN_PREFIX;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
User creds = new ObjectMapper()
.readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String token = Jwts.builder()
.setSubject(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}
JWTAuthorizationFilter.java
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 io.jsonwebtoken.Jwts;
import static com.app.mycompany.TasksApp.security.SecurityConstants.EXPIRATION_TIME;
import static com.app.mycompany.TasksApp.security.SecurityConstants.HEADER_STRING;
import static com.app.mycompany.TasksApp.security.SecurityConstants.SECRET;
import static com.app.mycompany.TasksApp.security.SecurityConstants.TOKEN_PREFIX;
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
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(HEADER_STRING);
if (token != null) {
// parse the token.
String user = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
Angular UI服务电话
我已经更改了api以使用HttpClient方法并观察响应。我仍然无法访问标题作为回应。
authenticate(credentials, callback) {
const AuthorizationHeader = credentials ? 'Basic ' + btoa(credentials.username + ':' + credentials.password) : '';
const serviceheaders = new HttpHeaders({'Content-Type': 'application/json', 'Authorization' : AuthorizationHeader});
// const headers = new HttpHeaders({
// 'Content-Type': 'application/json', 'Authorization' : AuthorizationHeader
// });
const username = credentials ? credentials.username : '';
const password = credentials ? credentials.password : '';
this.http.post<HttpResponse<any>>('http://localhost:8080/login', JSON.stringify({username: username, password: password})
, {headers: serviceheaders, observe: 'response', responseType: 'json'})
.subscribe( (response: HttpResponse<any>) => {
console.log('Headers only: ' + response.headers);
console.log('Response Headers : ' + response.headers.get('Authorization'));
const token = response.headers.get('Authorization');
if (token !== null) {
this.authenticated = true;
}
return callback && callback();
});
}
答案 0 :(得分:1)
我知道我来晚了,但是我在React + Spring Boot中遇到了类似的问题。通过在我的CORS配置中添加“ config.setExposedHeaders(Arrays.asList(“ Authorization”))“来解决此问题:
@Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
source.registerCorsConfiguration("/**", config.applyPermitDefaultValues());
config.setExposedHeaders(Arrays.asList("Authorization"));
return source;
}
在前端,我做了以下事情:
export default function ({ history }) {
const [login, setLogin] = useState('');
const [password, setpassword] = useState('');
async function handleLogin(e) {
e.preventDefault();
const response = await Api.post('/login', {
login, senha: password
});
console.log(response.headers);
if(response.headers['authorization']){
history.push("/menu");
}
}
如您所见,“ Authorization”标头使用小写字母。