I am working on a project that involves creating a rest service on Spring Boot that will eventually work with an Angular Web App and Discord Bot.
I am currently working on the backend and trying to unit test the endpoints. As evenually non-logged in users can make only make GET request. However, for some reason when ever I unit test an endpoint, it comes back as error 403. Even when I tell Spring Security to allow any request.
Security Config
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//http.authorizeRequests().antMatchers("/api/*").permitAll();
http.authorizeRequests().anyRequest().permitAll();
// .authorizeRequests()
// .antMatchers("/api/**/rule","/api/**/rules" )
// .permitAll()
// .anyRequest()
// .permitAll()
// .and()
// .formLogin()
// .loginPage("/login")
// .permitAll()
// .and()
// .logout()
// .permitAll();
}
#Autowired
public void configureAuth(AuthenticationManagerBuilder auth) throws Exception{
auth
.inMemoryAuthentication()
.withUser("admin").password("password").roles("ADMIN", "USER", "EDITOR").and()
.withUser("user").password("password").roles("USER", "EDITOR");
}
}
JUnit test
#RunWith(SpringRunner.class)
#SpringBootTest()
#AutoConfigureMockMvc
public class TestSFRuleController {
ObjectMapper mapper = new ObjectMapper();
#Autowired
MockMvc mvc;
#Autowired
SFRuleRepo repo;
#Before
public void setUp(){
repo.deleteAll();
}
#Test
#WithMockUser(username = "admin")
public void testInsertNewRule() throws Exception {
SFRule rule = new SFRule("test insert rule", "test desc");
String json = mapper.writeValueAsString(rule);
mvc.perform(
post(StarfinderController.PREFIX_URL + StarfinderController.RULE_URL)
.content(json))
.andExpect(status()
.isOk())
.andExpect(jsonPath("$.id").isNotEmpty())
.andExpect(jsonPath("$.name").value("test insert rule"));
}
}
Controller
#RestController
#RequestMapping("/api/sf")
public class StarfinderController {
#Autowired
SFRuleRepo ruleRepo;
public static final String PREFIX_URL = "/api/sf";
public static final String RULE_URL = "/rule";
public static final String RULES_URL = "/rules";
public static final String RULE_REPO = "RULE";
public static final String PAGE = "page";
public static final String COUNT = "count";
#GetMapping(RULE_URL)
public Rule getRule(#RequestParam(value = "name", required = true) String name) {
return ruleRepo.findByName(name);
}
#GetMapping(RULES_URL)
public List<Rule> getRules(#RequestParam(value = "tag", required = false, defaultValue = "") String tagName,
#RequestParam(value = "page", required = false) int page,
#RequestParam(value = "count", required = false) int count) {
if (!tagName.isEmpty()) {
// noop
} else {
//TODO: add support for page and count
List<Rule> list = new LinkedList<Rule>();
list.addAll(ruleRepo.findAll());
return list;
}
return null;
}
#PostMapping(RULE_URL)
public Rule addRule(SFRule rule) {
return ruleRepo.save(rule);
}
#PutMapping(RULE_URL)
public Rule updateRule(SFRule rule) {
Optional<SFRule> savedRule = ruleRepo.findById(rule.getId());
if (savedRule.isPresent()) {
SFRule sv = savedRule.get();
sv.setName(rule.getName());
sv.setDesc(rule.getDesc());
return ruleRepo.save(sv);
} else {
return null;
}
}
#DeleteMapping(RULE_URL)
public void deleteRule(SFRule rule) {
ruleRepo.delete(rule);
}
}
This should work, just put the URL in .antMatcher().permitAll()
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/targetURL/**").permitAll()
}
Related
I have a Spring Boot Application where an endpoint is secured with an API Key like this:
#Configuration
#EnableWebSecurity
#Order(1)
public class AuthConfiguration {
public static final String API_KEY_VALUE = "skrdgvsnelrkv";
public static final String API_KEY_HEADER = "API_KEY";
#Value(API_KEY_HEADER)
private String principalRequestHeader;
#Value(API_KEY_VALUE)
private String principalRequestValue;
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
AuthFilter apiKeyFilter = new AuthFilter(principalRequestHeader);
apiKeyFilter.setAuthenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (!principalRequestValue.equals(principal)) {
throw new BadCredentialsException(
"The API key was not found or not the expected value."
);
}
authentication.setAuthenticated(true);
return authentication;
}
});
http.antMatcher(Endpoints.VALIDATE)
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(apiKeyFilter)
.authorizeRequests()
.anyRequest()
.authenticated();
return http.build();
}
}
I have tests for that endpoint before, but now they understandably fail with a 403 Forbidden error. Here's how one of them looks like:
#AutoConfigureTestEntityManager
#SpringBootTest
#ContextConfiguration(classes = { TestContext.class })
#TestPropertySource(properties = { "spring.main.allow-bean-definition-overriding=true" })
#AutoConfigureMockMvc
class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
void callingValidateEndpointWithValidFileShouldReturnResponseWithStatusOk()
throws Exception {
MockMultipartFile file =
MockMultipathFileBuilder.buildFromFilePath(TestFiles.VALID_FILE);
mockMvc.perform(MockMvcRequestBuilders.multipart(Endpoints.VALIDATE).file(file))
.andExpect(status().isOk());
}
}
How do I need to adjust this test to make it pass?
All that is needed is to add the API Key as a header, like this:
.header(AuthConfiguration.API_KEY_HEADER, AuthConfiguration.API_KEY_VALUE)
That means the test should look like this:
#Test
void callingValidateEndpointWithValidFileShouldReturnResponseWithStatusOk()
throws Exception {
MockMultipartFile file =
MockMultipathFileBuilder.buildFromFilePath(TestFiles.VALID_FILE);
mockMvc.perform(
MockMvcRequestBuilders.multipart(Endpoints.VALIDATE)
.file(file)
.header(AuthConfiguration.API_KEY_HEADER, AuthConfiguration.API_KEY_VALUE)
).andExpect(status().isOk());
}
I'm updating an old application to use WebFlux but I've gotten a bit lost when it comes to handling JWT validation with Spring Security.
The existing code (which works with standard Spring Web) looks like:
(Validating a Firebase Token)
public class FirebaseAuthenticationTokenFilter extends AbstractAuthenticationProcessingFilter {
private static final String TOKEN_HEADER = "X-Firebase-Auth";
public FirebaseAuthenticationTokenFilter() {
super("/v1/**");
}
#Override
public Authentication attemptAuthentication(
final HttpServletRequest request, final HttpServletResponse response) {
for (final Enumeration<?> e = request.getHeaderNames(); e.hasMoreElements(); ) {
final String nextHeaderName = (String) e.nextElement();
final String headerValue = request.getHeader(nextHeaderName);
}
final String authToken = request.getHeader(TOKEN_HEADER);
if (Strings.isNullOrEmpty(authToken)) {
throw new RuntimeException("Invaild auth token");
}
return getAuthenticationManager().authenticate(new FirebaseAuthenticationToken(authToken));
}
However when switching to WebFlux we lose HttpServletRequest and HttpServletResponse. There is a GitHub issue which suggests there is an alternative method/fix https://github.com/spring-projects/spring-security/issues/5328 however following it through I'm not able to identify what was actually changed to make this work.
The Spring Security docs while great, don't really explain how to handle the use-case.
Any tips on how to proceed?
Got there in the end:
First need to update the filter chain with a custom filter just like before
#Configuration
public class SecurityConfig {
private final FirebaseAuth firebaseAuth;
public SecurityConfig(final FirebaseAuth firebaseAuth) {
this.firebaseAuth = firebaseAuth;
}
#Bean
public SecurityWebFilterChain springSecurityFilterChain(final ServerHttpSecurity http) {
http.authorizeExchange()
.and()
.authorizeExchange()
.pathMatchers("/v1/**")
.authenticated()
.and()
.addFilterAt(firebaseAuthenticationFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
.csrf()
.disable();
return http.build();
}
private AuthenticationWebFilter firebaseAuthenticationFilter() {
final AuthenticationWebFilter webFilter =
new AuthenticationWebFilter(new BearerTokenReactiveAuthenticationManager());
webFilter.setServerAuthenticationConverter(new FirebaseAuthenticationConverter(firebaseAuth));
webFilter.setRequiresAuthenticationMatcher(ServerWebExchangeMatchers.pathMatchers("/v1/**"));
return webFilter;
}
}
The main workhorse of the process is FirebaseAuthenticationConverter where I validate the incoming JWT against Firebase, and perform some standard logic against it.
#Slf4j
#Component
#RequiredArgsConstructor
public class FirebaseAuthenticationConverter implements ServerAuthenticationConverter {
private static final String BEARER = "Bearer ";
private static final Predicate<String> matchBearerLength =
authValue -> authValue.length() > BEARER.length();
private static final Function<String, Mono<String>> isolateBearerValue =
authValue -> Mono.justOrEmpty(authValue.substring(BEARER.length()));
private final FirebaseAuth firebaseAuth;
private Mono<FirebaseToken> verifyToken(final String unverifiedToken) {
try {
final ApiFuture<FirebaseToken> task = firebaseAuth.verifyIdTokenAsync(unverifiedToken);
return Mono.justOrEmpty(task.get());
} catch (final Exception e) {
throw new SessionAuthenticationException(e.getMessage());
}
}
private Mono<FirebaseUserDetails> buildUserDetails(final FirebaseToken firebaseToken) {
return Mono.just(
FirebaseUserDetails.builder()
.email(firebaseToken.getEmail())
.picture(firebaseToken.getPicture())
.userId(firebaseToken.getUid())
.username(firebaseToken.getName())
.build());
}
private Mono<Authentication> create(final FirebaseUserDetails userDetails) {
return Mono.justOrEmpty(
new UsernamePasswordAuthenticationToken(
userDetails.getEmail(), null, userDetails.getAuthorities()));
}
#Override
public Mono<Authentication> convert(final ServerWebExchange exchange) {
return Mono.justOrEmpty(exchange)
.flatMap(AuthorizationHeaderPayload::extract)
.filter(matchBearerLength)
.flatMap(isolateBearerValue)
.flatMap(this::verifyToken)
.flatMap(this::buildUserDetails)
.flatMap(this::create);
}
}
To the previous answer there could be added that this method also works fine:
private Mono<FirebaseToken> verifyToken(final String unverifiedToken) {
try {
return Mono.just(FirebaseAuth.getInstance().verifyIdToken(unverifiedToken));
} catch (final Exception e) {
throw new SessionAuthenticationException(e.getMessage());
}
}
And this one does not provid warnings regarding unnecessary use of blocking methods (like get())
I want to generate a JWT with expiration date for people to access the system without have to register and create a user. Is this posible? I have tried with JwtTokenProvider but it needs a LoginRequest to work also with Jwts.builder() also needs a user.
if you want to use spring security you can create security configration and extends WebSecurityConfigurerAdapter. Then important point is custom provider.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
private JWTConfigurer securityConfigurerAdapter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//you can write customAuth provider
auth.authenticationProvider(customAuthenticationProvider);
}
#Override
public void configure(WebSecurity web) throws Exception {
//Some ignore etc.
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf()
.disable().and()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
//important here
.antMatchers("/api/v1/authentication/**").permitAll()
.anyRequest().authenticated()
.and()
.apply(securityConfigurerAdapter);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return this.authenticationManager();
}
}
This is Filter class which extends genericFilterBean. Every request is monitored in this class
You will check to it is right token
I create token TokenProvider class and depend into JWTFilter then use valideToken method.
if token is sended and not validate then throw exception
if token is not sended then go super method so the flow is continue and works auth.authenticationProvider. Spring knows to start customAuthenticationProvider behind the scene becouse of you set into SecurityConfiguration class
#Component
public class JWTFilter extends GenericFilterBean {
private final Logger log = LoggerFactory.getLogger(JWTFilter.class);
#Autowired
private TokenProvider tokenProvider;
#Autowired
private MessageSource msgSource;
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
try {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
//Resolve method is optional what you want to use
String jwt = resolveToken(httpServletRequest);
if (StringUtils.hasText(jwt)) {
//token validation is important becouse of expires date into token
// and you will check expired date
if (this.tokenProvider.validateToken(jwt)) {
String jwtMd5 = DigestUtils.md5Hex(jwt);
MDC.put("jwt",jwtMd5);
Authentication authentication = this.tokenProvider.getAuthentication(jwt);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}catch(Exception ex){
handleException((HttpServletResponse) servletResponse,ex);
}
}
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(JWTConfigurer.AUTHENTICATION_HEADER);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
String jwt = bearerToken.substring(7, bearerToken.length());
return jwt;
}
String jwt = request.getParameter(JWTConfigurer.AUTHENTICATION_TOKEN);
if (StringUtils.hasText(jwt)) {
return jwt;
}
return null;
}
}
You can use this class for create token or validate token
you define expire date for token expiration into create method.
#Component public class TokenProvider {
private final Logger log = LoggerFactory.getLogger(TokenProvider.class);
private static final String AUTHORITIES_KEY = "auth";
private static final String WTS_USER_ID = "wtsUserId";
private static final String CHANNEL_PERMISSIONS = "channelPermissions";
private static final String APP_ROLES = "appRoles";
private String secretKey;
private long tokenValidityInSeconds;
#Autowired private ApplicationProperties applicationProperties;
#PostConstruct public void init() {
this.tokenValidityInSeconds = 1000;
}
public String createToken(Authentication authentication, Boolean rememberMe) { List<String> authorities = authentication.getAuthorities().stream().map(authority -> authority.getAuthority())
.collect(Collectors.toList());
//Token creation format is this
// token will be three part important parts are claims and sign
// claims refers to body to use datas
// sign will use to validation
return Jwts.builder().setSubject(authentication.getName()).claim(AUTHORITIES_KEY, authorities)
.claim(WTS_USER_ID, ((JWTAuthentication) authentication).getWtsUserId())
.claim(CHANNEL_PERMISSIONS, ((JWTAuthentication) authentication).getChannelPermissions())
.claim(APP_ROLES, ((JWTAuthentication) authentication).getAppRoles())
.signWith(SignatureAlgorithm.HS512, secretKey).setExpiration(tokenValidityInSeconds).compact(); }
#SuppressWarnings("unchecked") public Authentication getAuthentication(String token) { Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
List<String> list = (List<String>) claims.get(AUTHORITIES_KEY); Collection<? extends GrantedAuthority> authorities = list.stream()
.map(authority -> new SimpleGrantedAuthority(authority)).collect(Collectors.toList()); Integer wtsUserId = (Integer) claims.get(WTS_USER_ID); List<String> appRoles = (List<String>) claims.get(APP_ROLES);
ObjectMapper objectMapper = new ObjectMapper(); List<ChannelPermission> channelPermissions = objectMapper.convertValue(claims.get(CHANNEL_PERMISSIONS),
new TypeReference<List<ChannelPermission>>() {
});
return new JWTAuthentication(token, wtsUserId, claims.getSubject(), authorities, channelPermissions, appRoles); }
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken);
return true;
} catch (SignatureException e) {
log.info("Invalid JWT signature: " + e.getMessage());
return false;
} } }
This is controller who anonymous people get a JWT token .You can give a new JWT token all request and this JWT has expires date becouse of you set a expiration date into provider class.
#RequestMapping(value = "/login", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ApiResponse login(#RequestBody #Validated AuthenticationRequestDTO authenticationRequest) {
Authentication authentication = this.authenticationManager.authenticate(new JWTAuthentication(
RandomUid, RandomPwd, "anonymous"));
SecurityContextHolder.getContext().setAuthentication(authentication);
String token = tokenProvider.createToken(authentication, false);
return new ApiResponse(ApiResponseStatus.SUCCESS, new AuthenticationResponseDTO(token));
}
In our Spring Boot project we secured each method with #PreAuthorize annotation. It checks if user has permission for the requested resource.
Here is one of our controllers:
#PreAuthorize("#SecurityPermission.hasPermission('role')")
#RequestMapping(value = "/role")
public class RoleController {
#Autowired
private RoleService roleService;
#PreAuthorize("#SecurityPermission.hasPermission('role.list')")
#RequestMapping(value = "/allroles", method = RequestMethod.GET, consumes = "application/json", produces = "application/json")
public JsonData<Role> getListOfRoles() {
JsonData<Role> roleJsonData = new JsonData<>();
roleJsonData.setData(roleService.list());
return roleJsonData;
}
}
The question is: How to test properly permissions for above mentioned method?
I have tried the following two options:
#RunWith(SpringRunner.class)
#WebMvcTest(RoleController.class)
public class RoleControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private RoleService roleService;
#Test
public void optionOne() throws Exception {
ArrayList<Role> roles = new ArrayList<>();
roles.add(new Role().setId(1L).setName("administrator"));
roles.add(new Role().setId(2L).setName("user"));
given(roleService.list()).willReturn(roles);
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
this.mvc.perform(get("/role/allroles").with(user("testadmin"))
.headers(headers))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.data[0].name", is( roles.get(0).getName())))
.andExpect(jsonPath("$.data[1].name", is( roles.get(1).getName())));
}
#Test
#WithMockUser(authorities = {"role.list"})
public void optionTwo() throws Exception {
ArrayList<Role> roles = new ArrayList<>();
roles.add(new Role().setId(1L).setName("administrator"));
roles.add(new Role().setId(2L).setName("user"));
given(roleService.list()).willReturn(roles);
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
this.mvc.perform(get("/role/allroles")
.headers(headers))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.data[0].name", is( roles.get(0).getName())))
.andExpect(jsonPath("$.data[1].name", is( roles.get(1).getName())));
}
}
optionOne passes even though mock user doesn't have required permission("role.list") while optionTwo failes with the status 403.
java.lang.AssertionError: Status
Expected :200
Actual :403
UPDATE: I am adding WebSecurityConfig class
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";
public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/auth/login";
public static final String SEARCH_BASED_ENTRY_POINT = "/search/**";
public static final String TOKEN_REFRESH_ENTRY_POINT = "/auth/token";
public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/**";
#Autowired
private RestAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private AuthenticationSuccessHandler successHandler;
#Autowired
private AuthenticationFailureHandler failureHandler;
#Autowired
private AjaxAuthenticationProvider ajaxAuthenticationProvider;
#Autowired
private JwtAuthenticationProvider jwtAuthenticationProvider;
#Autowired
private TokenExtractor tokenExtractor;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private ObjectMapper objectMapper;
#Bean
protected AjaxLoginProcessingFilter buildAjaxLoginProcessingFilter() throws Exception {
AjaxLoginProcessingFilter filter = new AjaxLoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler, objectMapper);
filter.setAuthenticationManager(this.authenticationManager);
return filter;
}
#Bean
protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
List<String> pathsToSkip = Arrays.asList(TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT, SEARCH_BASED_ENTRY_POINT);
SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
JwtTokenAuthenticationProcessingFilter filter
= new JwtTokenAuthenticationProcessingFilter(failureHandler, tokenExtractor, matcher);
filter.setAuthenticationManager(this.authenticationManager);
return filter;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(ajaxAuthenticationProvider);
auth.authenticationProvider(jwtAuthenticationProvider);
}
#Bean
protected Md5PasswordEncoder passwordEncoder() {
return new Md5PasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("utf-8");
filter.setForceEncoding(true);
http.addFilterBefore(filter, CsrfFilter.class);
http.addFilterBefore(new WebSecurityCorsFilter(), ChannelProcessingFilter.class);
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(this.authenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll()
.antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll()
.antMatchers(SEARCH_BASED_ENTRY_POINT).permitAll()
.and()
.authorizeRequests()
.antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated()
.and()
.addFilterBefore(buildAjaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
I had the same problem a few months ago but in a slightly different way. I think your context is not setup correctly, since you have to apply SpringSecurity explicitiy to it for testing purposes:
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
You can also refer to: How to unit test a secured controller which uses thymeleaf (without getting TemplateProcessingException)?
It is slightly different to your problem, but since SecurityHandling is kind of an individual setup, it ist hard to help without knowing your project better.
If you are trying to test the behaviour for a non-authorized User, you can also do something like this:
#Test
public void getLoginSuccessWithAnonymousUserReturnsAccessDeniedException() throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/your-url").with(anonymous()))
.andExpect(status().is3xxRedirection()) //change to your code
.andReturn();
Class result = mvcResult.getResolvedException().getClass();
MatcherAssert.assertThat((result.equals(org.springframework.security.access.AccessDeniedException.class)), is(true));
}
I'm trying to create a server with OAuth 2 but I have a problem. I configured OAuth, the user can authorize and get a token but the REST methods are always accesible, for example a user can use method POST when they didn't authorize.
How to configure OAuth so the REST methods run only when a user did authorize?
This is how some of my code looks like (I used this example code):
OAuthConfiguration class
#Configuration
public class OAuth2ServerConfiguration {
private static final String RESOURCE_ID = "restservice";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
// #formatter:off
resources
.resourceId(RESOURCE_ID);
// #formatter:on
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.antMatchers("/users").hasRole("ADMIN")
.antMatchers("/greeting").authenticated();
// #formatter:on
}
}
AuthorizationServerConfiguration class:
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private TokenStore tokenStore = new InMemoryTokenStore();
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
// #formatter:off
endpoints
.tokenStore(this.tokenStore)
.authenticationManager(this.authenticationManager)
.userDetailsService(userDetailsService);
// #formatter:on
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients
.inMemory()
.withClient("clientapp")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret("123456");
// #formatter:on
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(this.tokenStore);
return tokenServices;
}
}
Rest controller:
#RestController
#RequestMapping("/ABC")
final class Controller {
#Autowired
Repository repository;
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
int create(#RequestBody #Valid Data myData) {
repository.create(myData);
return 1;
}
#RequestMapping(value = "{number}", method = RequestMethod.GET)
Data findByNumber(#PathVariable("number") String number) {
Data data = repository.findByNumber(number);
return data;
}
#RequestMapping(value = "{number}", method = RequestMethod.PUT)
int update(#RequestBody #Valid Data myData) {
int rows = repository.update(myData);
return 1;
}
#RequestMapping(value = "{number}", method = RequestMethod.DELETE)
int delete(#PathVariable("number") String number) {
repository.delete(serialNumber);
return 1;
}
}
You'll want to add .antMatchers("/ABC/**").authenticated()
See jhipster sample oauth2 example
https://github.com/jhipster/jhipster-sample-app-oauth2/blob/master/src/main/java/com/mycompany/myapp/config/OAuth2ServerConfiguration.java