I am trying to use the below class
#EnableWebSecurity
#Configuration
public class WebConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(
"/v2/api-docs",
"/swagger-resources/**",
"/swagger-ui.html",
"/webjars/**" ,
"/swagger.json").permitAll();
}
#Bean
public UserDetailsService userDetailsService() {
return super.userDetailsService();
}
}
to enable cors
When trying to write my Junit
#RunWith(JUnitPlatform.class)
public class WebConfigTest {
#InjectMocks
private WebConfig webConfig;
#Mock
private AuthenticationManagerBuilder auth = Mockito.mock(AuthenticationManagerBuilder.class);
#Mock
private HttpSecurity http;
#BeforeEach
public void setUp() throws Exception {
MockitoAnnotations.openMocks(this);
}
#Test
public void testConfigure() throws Exception {
System.out.println(http);
System.out.println(webConfig);
}
}
WebConfig and HttpSecurity are getting mocked, please suggest
Related
I am trying to update to Spring Boot 2.7.0-SNAPSHOT.
WebSecurityConfigurerAdapter is deprecated in this version.
Old WebSecurityConfig with WebSecurityConfigurerAdapter (working fine):
/**
* SecurityConfig
*
*/
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
#Autowired
private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
#Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
#Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
#Autowired
private OAuth2UserServiceImpl oAuth2UserServiceImpl;
/**
* for development
*
* #return
*/
#Bean
public PasswordEncoder passwordEncoder() {
// for development
return NoOpPasswordEncoder.getInstance();
}
#Override
public void configure(WebSecurity web) {
// ignoring
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**", "/favicon.ico", "/oauth2");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/login", "/error", "/message/**").permitAll();
http.authorizeRequests().anyRequest().authenticated();
http.formLogin();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint("/login"));
http.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
// OAuth2
http.oauth2Login().loginPage("/").defaultSuccessUrl("/home", false);
http.oauth2Login().userInfoEndpoint().userService(oAuth2UserServiceImpl);
http.oauth2Login().successHandler(oAuth2AuthenticationSuccessHandler);
http.oauth2Login().failureHandler(oAuth2AuthenticationFailureHandler);
http.logout().logoutUrl("/logout").logoutSuccessUrl("/login").deleteCookies("JSESSIONID");
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.enableSessionUrlRewriting(false);
}
private MyAuthenticationFilter authenticationFilter() throws Exception {
MyAuthenticationFilter filter = new MyAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
filter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/authentication", "POST"));
return filter;
}
private AuthenticationEntryPoint authenticationEntryPoint(String loginFormUrl) {
return new MyLoginUrlAuthenticationEntryPoint(loginFormUrl);
}
}
After reading this blog post, I have modified the new WebSecurityConfig:
/**
* SecurityConfig
*
*/
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
#Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
#Autowired
private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
#Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
#Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
#Autowired
private OAuth2UserServiceImpl oAuth2UserServiceImpl;
/**
* for development
*
* #return
*/
#Bean
public PasswordEncoder passwordEncoder() {
// for development
return NoOpPasswordEncoder.getInstance();
}
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers(
"/css/**", "/js/**", "/img/**", "/lib/**", "/favicon.ico", "/oauth2");
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/login", "/error", "/message/**").permitAll();
http.authorizeRequests().anyRequest().authenticated();
http.formLogin();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint("/login"));
http.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
// OAuth2
http.oauth2Login().loginPage("/").defaultSuccessUrl("/home", false);
http.oauth2Login().userInfoEndpoint().userService(oAuth2UserServiceImpl);
http.oauth2Login().successHandler(oAuth2AuthenticationSuccessHandler);
http.oauth2Login().failureHandler(oAuth2AuthenticationFailureHandler);
http.logout().logoutUrl("/logout").logoutSuccessUrl("/login").deleteCookies("JSESSIONID");
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.enableSessionUrlRewriting(false);
return http.build();
}
private MyAuthenticationFilter authenticationFilter() throws Exception {
MyAuthenticationFilter filter = new MyAuthenticationFilter();
// How can I fix this? ------------------------------------------
filter.setAuthenticationManager(authenticationManagerBean());
// --------------------------------------------------------------
filter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
filter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/authentication", "POST"));
return filter;
}
private AuthenticationEntryPoint authenticationEntryPoint(String loginFormUrl) {
return new MyLoginUrlAuthenticationEntryPoint(loginFormUrl);
}
}
I was able to fix two methods.
(#configure(WebSecurity web) and #configure(HttpSecurity http))
However, I can't figure out how to fix authenticationManagerBean().
Where do I get the AuthenticationManager from?
Here's the entire class that took me day to configure. Hope it could save your time.
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class ApplicationSecurityConfig {
private final UserDetailsService userService;
private final AuthenticationConfiguration configuration;
#Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
AbstractAuthenticationProcessingFilter filter = new CustomizedAuthenticationFilter(authenticationManager());
filter.setFilterProcessesUrl("/api/login");
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers(HttpMethod.POST, "/api/login").permitAll();
http.authorizeRequests().anyRequest().authenticated();
http.addFilter(filter);
return http.build();
}
#Bean
AuthenticationManager authenticationManager() throws Exception {
return configuration.getAuthenticationManager();
}
#Autowired
void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
}
You can create a custom DSL. This is actually how Spring Security works internally.
public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
#Override
public void configure(HttpSecurity http) throws Exception {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.addFilterBefore(new MyAuthenticationFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);
}
public static MyCustomDsl customDsl() {
return new MyCustomDsl();
}
}
Then you can apply the custom DSL when building the SecurityFilterChain:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
http.apply(customDsl());
return http.build();
}
If you want to inject AuthenticationManager into other spring components, you can use the following configuration.
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
This approach has solved the problem for me and you can inject AuthenticationManager wherever you need.
EDIT:
#EnableWebSecurity
#RequiredArgsConstructor
public class SpringSecurityConfiguration {
private final AuthenticationConfiguration authenticationConfiguration;
#Bean
public AuthenticationManager authenticationManager() throws Exception
{
return authenticationConfiguration.getAuthenticationManager();
}
I am working on authentication service part of cloud app and I created the following security config class.
#Configuration
#EnableWebSecurity
public class JwtSecurityConfig extends WebSecurityConfigurerAdapter {
private final PasswordEncoder encoder;
private final UserService userService;
private final JwtConstant jwtConstant;
#Autowired
public JwtSecurityConfig(PasswordEncoder encoder, UserService userService, JwtConstant jwtConstant) {
this.encoder= encoder;
this.userService = userService;
this.jwtConstant = jwtConstant;
}
#Bean
public DaoAuthenticationProvider getAuthenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(encoder);
authenticationProvider.setUserDetailsService(userService);
return authenticationProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(getAuthenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(getAuthenticationFilter())
.authorizeRequests()
.antMatchers(HttpMethod.PUT, "/signup").permitAll()
.anyRequest()
.authenticated();
}
private AuthenticationFilter getAuthenticationFilter() throws Exception {
return new AuthenticationFilter(authenticationManager(), jwtConstant);
}
}
I am not sure about the chain methods of configure(HttpSecurity http) method. The authentication service will only receive "login" and "signup" requests.
Should I remove authorizeRequests() method as I do not authorize anything?
I am not sure about anyRequest().authenticated() part either if I really need it?
there are a couple of things that have to be changed, but first of all, you have to define a method that will provide jwt for each request and every request should provide an AuthRequest object that contains username and password :
#RestController
public class WelcomeController {
#Autowired
private JwtUtil jwtUtil;
#Autowired
private AuthenticationManager authenticationManager;
#PostMapping("/signup")
public String generateToken(#RequestBody AuthRequest authRequest) throws Exception {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUserName(), authRequest.getPassword())
);
} catch (Exception ex) {
throw new Exception("inavalid username/password");
}
return jwtUtil.generateToken(authRequest.getUserName());
}
}
and in the UserDetailsService you can make authentication as below :
#Service
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
#Autowired
private final UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("tried to loging : " + username);
if(!Objects.isNull(username) && !"".equals(username)){
Optional<User> user = userRepository.findUserByUserName(username);
System.out.println(user.get());
if(user.isPresent()){
User userParam = user.get();
return new org.springframework.security.core.userdetails.User(userParam.getUserName(),
userParam.getPassword(), new ArrayList<>());
}
}
throw new UsernameNotFoundException("user does not exists or empty !!");
}
}
and for the configuration side :
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private final UserDetailsService userDetailsService;
#Autowired
private final JwtFilter jwtFilter;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder(10);
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/signup").permitAll()
.anyRequest().authenticated()
.and().exceptionHandling().and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);;
}
}
for further information, you can follow my Github branch Authnticaition sample
How can I define a #RequestMapping method to explicit allow anonymous (unauthorized) access?
The following does not work, always getting 401 Unauthorized:
#RequestMapping("/test")
#Secured(value={"ROLE_ANONYMOUS"})
public String test() {
return "OK";
}
In general the whole application is secured as follows, using spring-boot:
security.basic.enabled=true.
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
You can override configure(HttpSecurity httpSecurity) method and define your rules there:
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
httpSecurity.authorizeRequests()
.antMatchers("/test")
.permitAll();
super.configure(http);
}
}
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
httpSecurity.authorizeRequests().regexMatcher("^((?!test).)*$").permitAll();
}
}
I am not sure about a slash before test but just try this negative look-around method.
I am trying to follow this tutorial on setting up OAuth authentication in my web application I have just started, I am fairly new to spring and have been scratching my head over as to why the OAuth2AuthorisationServerConfig class cannot pick up the bean.
#Configuration
public class ServerSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("JL").password("TEST").roles("USER");
}
#Override
#Bean //Here is the bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/login", "/register").permitAll()
.anyRequest().authenticated()
.and().formLogin().permitAll()
.and().logout().permitAll();
}
}
#Configuration
public class OAuth2AuthorisationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean") // here is the qualifier for bean
private AuthenticationManager authenticationManager;
....
}
The two classes are in the same package
On the OAuth2AuthorisationServerConfig class I had annotated it with #ComponentScan and this seemed to have solved the problem
I am trying to configure Spring Security and OAuth2 on java config. I am using Spring Security version 4.0.4.RELEASE and OAuth2 version 2.0.11.RELEASE.
Spring Security config works well. Also I can get an access token with OAuth2 AuthorizationServer, but my ResourceServer does not work correctly. When I set the annotation #EnableResourceServer I can only check my access token and other URLs I cannot open (Security configuration and AuthorizationServer configuration do not work). I see the following error:
<oauth>
<error_description>
An Authentication object was not found in the SecurityContext
</error_description>
<error>unauthorized</error>
</oauth>
If I remove an annotation #EnableResourceServer, my ResourceServer does not check an access token. It just redirects to the authentication page.
This is my code:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class GlobalSecurityConfig extends GlobalMethodSecurityConfiguration {
#Bean(name = "passwordEncoder")
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
#Qualifier("authUserDetailsService")
private UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Autowired
#Qualifier("permissionEvaluator")
private PermissionEvaluator permissionEvaluator;
#Bean
public DefaultMethodSecurityExpressionHandler expressionHandler() {
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setDefaultRolePrefix("");
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return expressionHandler();
}
}
SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean(name = "clientAuthenticationEntryPoint")
public OAuth2AuthenticationEntryPoint oauthAuthenticationEntryPoint() {
OAuth2AuthenticationEntryPoint entry = new OAuth2AuthenticationEntryPoint();
entry.setRealmName("myapp/client");
entry.setTypeName("Basic");
return entry;
}
#Autowired
#Qualifier("webExpressionHandler")
private DefaultWebSecurityExpressionHandler expressionHandler;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/html/**", "/webapi/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/admin/**", "/**")
.and()
.authorizeRequests()
.expressionHandler(expressionHandler)
.antMatchers("/admin/**").access("hasRole('ADMINISTRATOR')")
.antMatchers("/1/admin/**").access("hasRole('ADMINISTRATOR')")
.antMatchers("/profile**").authenticated()
.antMatchers("/oauth/authorize").authenticated()
.and()
.formLogin().loginPage("/login")
.failureUrl("/login?error=1")
.loginProcessingUrl("/login-attempt")
.defaultSuccessUrl("/", false)
.and()
.sessionManagement()
.sessionFixation().migrateSession()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied")
.and()
.csrf();
}
}
Oauth config:
#Configuration
public class Oauth {
#Configuration
#EnableResourceServer
public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_oauth_server";
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.regexMatchers("/api/v0/.*").authenticated()
.antMatchers("/**").denyAll()
;
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private AuthorizationCodeServices verificationCodeService;
#Autowired
#Qualifier("clientDetails")
private ClientDetailsService clientDetailsService;
#Autowired
#Qualifier("tokenStore")
private TokenStore tokenStore;
#Bean(name = "tokenServices")
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore);
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService);
return tokenServices;
}
#Bean
public ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter() throws Exception {
ClientCredentialsTokenEndpointFilter filter = new ClientCredentialsTokenEndpointFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
endpoints.authorizationCodeServices(verificationCodeService);
endpoints.tokenServices(tokenServices());
endpoints.reuseRefreshTokens(true);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()");
oauthServer.checkTokenAccess("permitAll()");
oauthServer.realm("myapp/client");
oauthServer.addTokenEndpointAuthenticationFilter(clientCredentialsTokenEndpointFilter());
oauthServer.allowFormAuthenticationForClients();
}
}
}
So, ResourceServer config overlap other configuration. How can I fix it? I would be thankful for any help.
I see that you want to protect some endpoints with an access token, and other endpoints with normal form login.
Can you try restricting the applicability of your ResourceServerConfiguration to apply only to certain endpoints by something like: http.requestMatcher(new AntPathRequestMatcher("/api/v0/**")).... Do the same for SecurityConfig but for the endpoints you want it to take care of.