I am using spring boot 1.5.6 with OAuth 2.0. The problem is that when I run below test case, I get Null pointer exception on below line for authentication object.
System.out.println(authentication.getName());
Test Class
#RunWith(SpringRunner.class)
#SpringBootTest(classes = WebApp.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestPropertySource( locations = "classpath:test-application.properties")
public class ProfileControllerTest {
......
......
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
...........
.........
}
#Test
public void profileTest() throws Exception {
String userId = "12345678";
mockMvc.perform(get("user/" + userId + "/profile"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
}
}
Rest Controller
#RequestMapping(value = "user/{userId}/profile",
method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String, Object>> getProfile(
#PathVariable(value = "userId", required = true) String userId,
HttpServletRequest request, Authentication authentication) {
System.out.println(authentication.getName());
}
Security Configuration
#Configuration
#EnableOAuth2Sso
#EnableGlobalMethodSecurity(securedEnabled = true)
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String OAUTH_COOKIE = "OAUTH-ACCESS-TOKEN";
private UserService userService;
private OAuth2TokenAuthenticationFilter ssoFilter;
private JwtAccessTokenConverter accessTokenConverter;
/**
* WebAppSecurity overloaded constructor.
*/
public WebAppSecurityConfig(UserService userService,
OAuth2TokenAuthenticationFilter ssoFilter,
JwtAccessTokenConverter accessTokenConverter) {
this.userService = userService;
this.ssoFilter = ssoFilter;
this.accessTokenConverter = accessTokenConverter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.anonymous()
.disable()
.authorizeRequests()
.antMatchers("/logout-success")
.permitAll()
.anyRequest()
.authenticated()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterAfter(ssoFilter,
AbstractPreAuthenticatedProcessingFilter.class).logout()
.logoutUrl("/logout").logoutSuccessUrl("/logout-success").permitAll()
.deleteCookies(OAUTH_COOKIE);
}
#Autowired
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
configure((DefaultAccessTokenConverter) accessTokenConverter
.getAccessTokenConverter());
}
protected void configure(DefaultAccessTokenConverter accessTokenConverter) {
DefaultUserAuthenticationConverter userTokenConverter =
new DefaultUserAuthenticationConverter();
userTokenConverter.setUserDetailsService(userDetailsService());
accessTokenConverter.setUserTokenConverter(userTokenConverter);
}
#Override
protected UserDetailsService userDetailsService() {
return userService;
}
How to pass Authentication object in above test case?
You can use the #WithMockUser annotation on your test.
For example:
#Test
#WithMockUser(username = "admin", roles = { "ADMIN" })
public void profileTest() throws Exception {
String userId = "12345678";
mockMvc.perform(get("user/" + userId + "/profile"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
}
Assuming you have spring-security configured and enabled for the "user/" + userId + "/profile" endpoint then System.out.println(authentication.getName()); in your controller will print: admin.
More details in the Spring Securty docs.
Related
Hello everyone hope you doing well,
i have problem using open authentication in spring boot, when accessing page rest with postman is not even using param access token it still show the result, this my code please help???
Authorization Server Config class:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends
AuthorizationServerConfigurerAdapter{
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler);
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// TODO Auto-generated method stub
clients.inMemory()
.withClient("admin").secret("123")
.scopes("read","write")
.authorizedGrantTypes("password","refresh_token")
.accessTokenValiditySeconds(5*60)
.refreshTokenValiditySeconds(10*60);
}
}
Resource Server Config
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter{
#Override
public void configure(HttpSecurity http)throws Exception{
http
.anonymous().disable()
.authorizeRequests().antMatchers("/api/**") /** this
.authenticated()
.and()
.exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
Security Config
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private SecurityUtility hash;
#Autowired
private ClientDetailsService clientDetailsService;
private static final String[] PUBLIC_MATCHERS = { "/", "/css/**", "/image/**", "/js/**", "/newUser",
"/forgetPassword", "/login", "/logout", "/fonts/**", "/signUp", "/register", "/sendEmail", "/logout", "/tes","/oauth2/**","/api/**",
"/admin/tes","/SpringSecurityOAuth2Example/**",
"/admin/tes2" };
private static final String[] ADMIN_MATCHERS = { "/admin", "/admin/**" };
private static final String[] OAUTH2_PAGE = { "/oauth/**", "/api/**" };
private final String USERS_QUERY = "select username, password, is_enabled from user where username=?";
private final String ROLES_QUERY = "select u.username, u.is_enabled, r.name as authority from user u "
+ "inner join user_role ur on (u.id = ur.user_id) " + "inner join role r on (ur.role_id = r.roleid) "
+ "where username=?";
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers(PUBLIC_MATCHERS).permitAll().anyRequest().authenticated().and().formLogin()
.loginPage("/login").loginProcessingUrl("/app-login").usernameParameter("app_username")
.passwordParameter("app_password").defaultSuccessUrl("/myAccount").permitAll()
.and().logout().logoutSuccessUrl("/login")
.permitAll();
http.authorizeRequests().antMatchers(ADMIN_MATCHERS).hasRole("ADMIN");
// http.csrf().disable();
http.csrf().ignoringAntMatchers(OAUTH2_PAGE);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// temporary
// auth.inMemoryAuthentication().withUser("admin").password("admin").roles("test");
auth.jdbcAuthentication().usersByUsernameQuery(USERS_QUERY).authoritiesByUsernameQuery(ROLES_QUERY)
.dataSource(dataSource).passwordEncoder(hash.passwordEncoder());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
Auth Controller
#RestController
#EnableResourceServer
public class AuthController {
#GetMapping("/api/demo1")
public String apiTes() {
System.out.println("sysout mas");
return "return result";
}
}
solved guys, it because i was using springboot 1.5.10 so i have to add
security.oauth2.resource.filter-order=3
to spring application.properties
I want to exclude /login url from being authenticated by spring security.
My configuration class looks like'
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests().antMatchers("/v1/pricing/login").permitAll()
.antMatchers("v1/pricing/**").authenticated().and()
.addFilterBefore(corsFilter,UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v1/pricing/login");
}
JwtAuthenticationFilter looks like
- commented the exception part, as it starts throwing exception in login also
#Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
#Autowired
JwtTokenProvider jwtTokenProvider;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && jwtTokenProvider.validateToken(jwt)) {
String[] userInfo = jwtTokenProvider.getUserDetailsFromJWT(jwt);
UserDetails userDetails = new UserPrincipal(Long.parseLong(userInfo[0]), userInfo[1], userInfo[2], null,
userInfo[3]);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, null);
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if (StringUtils.hasText(token)) {
return token;
} /*else {
throw new AuthenticationServiceException("Authorization header cannot be blank!");
}*/
return null;
}
}
Any request with /v1/pricing/login still goes to JWtAuthentication filter and fails.
JwtTokenAuthenticationProcessingFilter filter is configured to skip following endpoints: /api/auth/login and /api/auth/token. This is achieved with SkipPathRequestMatcher implementation of RequestMatcher.
public class SkipPathRequestMatcher implements RequestMatcher {
private OrRequestMatcher matchers;
private RequestMatcher processingMatcher;
public SkipPathRequestMatcher(List<String> pathsToSkip, String processingPath) {
Assert.notNull(pathsToSkip);
List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList());
matchers = new OrRequestMatcher(m);
processingMatcher = new AntPathRequestMatcher(processingPath);
}
#Override
public boolean matches(HttpServletRequest request) {
if (matchers.matches(request)) {
return false;
}
return processingMatcher.matches(request) ? true : false;
}
}
Then call :
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";
public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";
protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
List<String> pathsToSkip = Arrays.asList(TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_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();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(ajaxAuthenticationProvider);
auth.authenticationProvider(jwtAuthenticationProvider);
}
#Bean
protected BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // We don't need CSRF for JWT based authentication
.exceptionHandling()
.authenticationEntryPoint(this.authenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point
.antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point
.antMatchers("/console").permitAll() // H2 Console Dash-board - only for testing
.and()
.authorizeRequests()
.antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected API End-points
.and()
.addFilterBefore(buildAjaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
I have a simple application that I have setup with spring security using a custom MySql Database. Now I'm writing test cases for it and they seems to fail on login page and anything that works after the login. My question is how do I write test cases for it to check the successful login and the subsequent requests?
My Security Config:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private DataSource dataSource;
#Value("${spring.queries.users-query}")
private String usersQuery;
#Value("${spring.queries.roles-query}")
private String rolesQuery;
#Autowired
private CustomAuthenticationSuccessHandler successHandler;
/** Providing the queries and data source for security*/
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception
{
auth.
jdbcAuthentication()
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(rolesQuery)
.dataSource(dataSource)
.passwordEncoder(bCryptPasswordEncoder);
}
/** Defining fine grained access for ADMIN and CUSTOMER user */
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/registration").permitAll()
.antMatchers("/user/**").hasAuthority(AppRole.CUSTOMER.toString())
.antMatchers("/health/**").hasAuthority(AppRole.ADMIN.toString())
.antMatchers("/admin/**").hasAuthority(AppRole.ADMIN.toString()).anyRequest()
.authenticated().and().csrf().disable().formLogin()
.loginPage("/login").failureUrl("/login?error=true")
.successHandler(successHandler)
.usernameParameter("username")
.passwordParameter("password")
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and().exceptionHandling()
.accessDeniedPage("/access-denied");
}
/** Defining ant matchers that should ignore the paths and provide no access to any one */
#Override
public void configure(WebSecurity web) throws Exception
{
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
}
}
My Custom Success Handler:
#Component
#Configuration
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler
{
/** Getting reference to UserService */
#Autowired
private UserService userService;
#Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Authentication authentication)
throws IOException, ServletException, RuntimeException
{
HttpSession session = httpServletRequest.getSession();
User authUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
com.crossover.techtrial.java.se.model.User user = userService.findUserByUsername(authUser.getUsername());
session.setAttribute("userId", user.getUserId());
session.setAttribute("username", authUser.getUsername());
session.setAttribute("accountId", user.getAccountId());
//set our response to OK status
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
authorities.forEach(authority ->
{
if(authority.getAuthority().equals(AppRole.ADMIN.toString()))
{
session.setAttribute("role", AppRole.ADMIN);
try
{
//since we have created our custom success handler, its up to us to where
//we will redirect the user after successfully login
httpServletResponse.sendRedirect("/admin/home");
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
else if (authority.getAuthority().equals(AppRole.CUSTOMER.toString()))
{
session.setAttribute("role", AppRole.CUSTOMER);
try
{
//since we have created our custom success handler, its up to us to where
//we will redirect the user after successfully login
httpServletResponse.sendRedirect("/user/home");
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
});
}
}
After some seraching I tried to write test cases like this but they don't seem to be working:
#RunWith(SpringRunner.class)
#SpringBootTest
public class TrialApplicationTests
{
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private FilterChainProxy springSecurityFilterChain;
#Autowired
private MockHttpServletRequest request;
private MockMvc mockMvc;
#Test
public void contextLoads()
{
}
#Before
public void setup()
{
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilters(springSecurityFilterChain)
.build();
}
#Test
public void verifiesLoginPageLoads() throws Exception
{
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.model().hasNoErrors())
.andExpect(MockMvcResultMatchers.view().name("login"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
#Test
public void testUserLogin() throws Exception
{
HttpSession session = mockMvc.perform(post("/login")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("username", "test")
.param("password", "test123")
)
.andExpect(MockMvcResultMatchers.status().isOk())
//.andExpect(redirectedUrl("/user/home"))
.andReturn()
.getRequest()
.getSession();
request.setSession(session);
SecurityContext securityContext = (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
SecurityContextHolder.setContext(securityContext);
}
#Test
public void testRetrieveUserBookings() throws Exception
{
testUserLogin();
mockMvc.perform(MockMvcRequestBuilders.get("user/bookings"))
.andExpect(MockMvcResultMatchers.model().hasNoErrors())
.andExpect(MockMvcResultMatchers.model().attributeExists("bookings"))
.andExpect(MockMvcResultMatchers.view().name("user/bookings"))
.andExpect(content().string(containsString("Booking")));
}
}
I searched on the net and there are links WithMockUser and UserDetails, but the problem is as you can see I'm setting a my primary key userId in the session in my custom success handler. So I would also need to get the session in my test. Please tell me the simplest way to write tests that will work, possibly with code since I'm new with security and all such.
UPDATE:
I changed the code as suggested but still getting the 404 error on my testRetrieveUserBookings. Any more ideas?
#RunWith(SpringRunner.class)
#ContextConfiguration
#SpringBootTest
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
#TestExecutionListeners(listeners={ServletTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
WithSecurityContextTestExecutionListener.class})
public class TrialApplicationTests
{
#Autowired
private WebApplicationContext webApplicationContext;
MockMvc mockMvc;
#Autowired
ForestApiClient apiClient;
#Autowired
AccountClient accountClient;
#Autowired
AirlineClient airlineClient;
#Autowired
UserService userService;
private final String INTEGRATION_ACCOUNT = "account1";
private MockHttpSession mockSession;
private Authentication authentication;
#Test
public void contextLoads()
{
}
#Before
public void setup()
{
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
//.addFilters(springSecurityFilterChain)
.build();
mockSession = new MockHttpSession(webApplicationContext.getServletContext(), UUID.randomUUID().toString());
mockSession.setAttribute("userId", 3);
mockSession.setAttribute("accountId", "ZWR26539");
}
#Test
public void testVerifiesLoginPageLoads() throws Exception
{
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.model().hasNoErrors())
.andExpect(MockMvcResultMatchers.view().name("login"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
#Test
public void testRegistration() throws Exception
{
mockMvc.perform(post("/registration")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("username", "test2")
.param("password", "test123")
.param("email", "crossovertestuser#gmail.com")
.param("address", "Some Address")
.param("accountCurrency", "USD")
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.model().hasNoErrors())
.andExpect(MockMvcResultMatchers.model().attributeExists("user"))
.andExpect(MockMvcResultMatchers.view().name("registration"))
.andExpect(content().string(containsString("User has been registered successfully")));
}
#Test
#WithMockUser(username="test",roles={"USER","ADMIN"})
public void testRetrieveUserBookings() throws Exception
{
mockMvc.perform(MockMvcRequestBuilders.get("user/bookings"))
.andExpect(MockMvcResultMatchers.model().hasNoErrors())
.andExpect(MockMvcResultMatchers.model().attributeExists("bookings"))
.andExpect(MockMvcResultMatchers.view().name("user/bookings"))
.andExpect(content().string(containsString("Booking")));
}
}
If your problem is only getting session inside the test, then you can go for MockHttpSession.
#Before
public void setUp() throws Exception {
mock = MockMvcBuilders.webAppContextSetup(wac).addFilters(springSecurityFilterChain).build();
MockHttpSession httpSession = new MockHttpSession(webAppContext.getServletContext(), UUID.randomUUID().toString());
}
#Test
public void test1(){
mock.perform(get("/").session(mockSession)).perfor();
}
I have two rules, the first one every url from oauth/** should be without security and and other url have to security. But now all urls are secure include url from oauth/**.
This is my security config rule.
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// JWT dont need CSRF
httpSecurity.csrf().disable().exceptionHandling().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
.antMatchers("oauth/**").permitAll().and()
.addFilterBefore(new JwtAuthenticationTokenFilter(), BasicAuthenticationFilter.class);
// disable page caching
httpSecurity.headers().cacheControl();
}
}
when I request the url http://localhost:8080/oauth/fb that is enter my JwtAuthenticationTokenFilter, and I want this url don't enter this filter.
You can override configure method with WebSecurity parameter.
#Override
public void configure(final WebSecurity web) throws Exception
{
web.ignoring().antMatchers("oauth/**");
}
This method should be used when serving static content such as css/* js/*, suggested in the documentation, however I couldn't find another way to permit URL mapping with custom filter in Spring Security.
<security:http pattern="/support/**" security="none"/>
You would probably need to write the Java equivalent of the above XML configuration. Basically, you are setting a new filter chain with no security for the above pattern.
I faced a similar problem. My security config:
// ... imports
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
private final PasswordEncoder passwordEncoder;
private final JwtFilter jwtFilter;
#Autowired
public SecurityConfig(#Qualifier("userDetailsServiceImpl") UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder,
JwtProvider jwtProvider) {
this.userDetailsService = userDetailsService;
this.passwordEncoder = passwordEncoder;
this.jwtFilter = new JwtFilter(jwtProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/auth/**").permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
protected DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder);
return provider;
}
}
And my security filter:
// ... imports
public class JwtFilter extends GenericFilterBean {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
public static final int TOKEN_START_POSITION = 7;
private final JwtProvider jwtProvider;
#Autowired
public JwtFilter(JwtProvider jwtProvider) {
this.jwtProvider = jwtProvider;
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
String token = getTokenFromRequest((HttpServletRequest) servletRequest);
if (token != null && jwtProvider.validateToken(token)) {
Map<String, Object> properties = jwtProvider.getUserPropertiesFromToken(token);
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
properties.get("login"),
null,
(Set<GrantedAuthority>) properties.get("authirities"));
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(servletRequest, servletResponse);
}
private String getTokenFromRequest(HttpServletRequest request) {
String bearer = request.getHeader(AUTHORIZATION_HEADER);
if (bearer != null && bearer.startsWith(TOKEN_PREFIX)) {
return bearer.substring(TOKEN_START_POSITION);
}
return null;
}
}
The reason my code didn't work for me was that I skipped the line filterChain.doFilter(servletRequest, servletResponse); in my filter, i.e. I didn't pass on the request and response to the next entity in the chain.
I try to implement OAuth2 client using authorization-code grant flow by spring-boot.
But it does not work.
"http://external_server/oauth/authorize" was called, but no GET arguments added.
Does anyone know what is wrong in below configuration?
Auth provider is implemented by doorkeeper and it's already working.
so URL constants in WebSecurityConfiguration are correct.
#Configuration
#EnableWebMvcSecurity
#EnableOAuth2Client
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String AUTH_ENDPOINT = "http://external_server";
private static final String LOGIN_URL = AUTH_ENDPOINT + "/users/sign_in";
private static final String LOGOUT_URL = AUTH_ENDPOINT + "/sign_out";
private static final String AUTH_URL = AUTH_ENDPOINT + "/oauth/authorize";
private static final String ACCESS_TOKEN_URL = AUTH_ENDPOINT + "/oauth/token";
#Autowired OAuth2ClientContext oAuth2ClientContext;
/**
* for specific api
*/
#Bean public RestTemplate restTemplate() {
return new RestTemplate();
}
/**
* for accessing protected resource
*/
#Bean public OAuth2RestTemplate oAuth2RestTemplate() {
return new OAuth2RestTemplate(resource(), oAuth2ClientContext);
}
#Bean protected OAuth2ProtectedResourceDetails resource() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setClientId("_xxx_");
resource.setClientSecret("_yyy_");
resource.setUserAuthorizationUri(AUTH_URL);
resource.setAccessTokenUri(ACCESS_TOKEN_URL);
return resource;
}
#Override public void configure(WebSecurity web) throws Exception {
web.debug(true).ignoring().antMatchers("/webjars/**", "/css/**");
}
#Override protected void configure(HttpSecurity http) throws Exception {
//#formatter:off
http.csrf().disable().authorizeRequests()
.antMatchers("/", "/callback")
.permitAll()
.anyRequest()
.authenticated();
http.formLogin()
.loginPage(AUTH_URL)
.loginProcessingUrl(LOGIN_URL);
http.httpBasic()
.disable();
//#formatter:on
}
}
By default only POST Method is enabled. You may need to include GET Method on AuthorizationConfig.
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
Will be like this:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
....
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints){
endpoints.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
}
On source code of Spring Oauth we have:
private Set<HttpMethod> allowedTokenEndpointRequestMethods() {
// HTTP POST should be the only allowed endpoint request method by default.
if (allowedTokenEndpointRequestMethods.isEmpty()) {
allowedTokenEndpointRequestMethods.add(HttpMethod.POST);
}
return allowedTokenEndpointRequestMethods;
}