I want two kinds of requests in my REST server:
Those who has the path "/freerest/" anyone can request, others will need authentication.
This is my code:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#Configuration
class WebSecurityConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Autowired
UserAccountRepository userAccountRepository;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
#Bean
UserDetailsService userDetailsService() {
return new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
UserAccount account = userAccountRepository.findByEmail(email);
if(account != null) {
return new User(account.getEmail(), account.getPassword(), true, true, true, true,
AuthorityUtils.createAuthorityList("USER"));
} else {
throw new UsernameNotFoundException("could not find the user '"
+ email + "'");
}
}
};
}
}
#EnableWebSecurity
#Configuration
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/freerest/**").permitAll().and().authorizeRequests().anyRequest().hasAnyAuthority("USER");
}
}
In my mind after hasAnyAuthority("USER"), should have a .permitAll(). But dont.
So, the freerest path works fine, but if I try some user, which is on my database, or the default Spring's user I get 403.
What is wrong?
Try this. You have added an and() inbetween the antMatch ans any Request. I think that is the problem.
And also add the correct authenticating realm followed by an and() as shown below. here I use the HTTP Basic Authentication for restful
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{
......
......
......
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/freerest/**").permitAll()
.anyRequest().hasAnyAuthority("USER")
.and()
.httpBasic();
}
......
......
......
}
Related
I have spring web mvc project with Spring Security 4.1.0.RELEASE
In spring controller i try fetch the user from the context
#RestController
public class Test {
#RequestMapping(value="test", method = RequestMethod.POST)
public ResponseEntity<Void> test() {
ContextUser user = (ContextUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
user is an instance with id = 0, login = null .... It is empty instance.
org.springframework.security.core.Authentication isAuthenticated = true, list of Collection<? extends GrantedAuthority> are correct This behaviour is occurred periodically. It is not every time for this request. I catch this issue only for that request
My configurations
#Configuration
#ComponentScan(basePackages={"com.test.app"},
excludeFilters=#ComponentScan.Filter(type=FilterType.REGEX, pattern={"com.test.app.web.*"}))
#PropertySource(value = { "classpath:application.properties" })
#EnableAspectJAutoProxy
public class AppConfig {
#Autowired
private DataSource dataSource;
//My beans
}
#Component
public class TestUserDetailsService implements UserDetailsService{
#Autowired
private TestUserService service;
#Override
public UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException {
User user = service.findByLogin(userName);
if (user == null) {
throw new UsernameNotFoundException("Error");
}
return new ContextUser(user);
}
}
public class ContextUser extends User implements UserDetails {
//...
}
#Configuration
#EnableWebSecurity
#EnableAsync
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private TestAuthenticationEntryPoint testAuthenticationEntryPoint;
#Autowired
private TestSimpleUrlAuthenticationSuccessHandler testSimpleUrlAuthenticationSuccessHandler;
#Autowired
private TestSimpleUrlAuthenticationFailureHandler testSimpleUrlAuthenticationFailureHandler;
#Autowired
private LogoutSuccessHandler logoutSuccessHandler;
#Autowired
private TestUserDetailsService testUserDetailsService;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder registry) throws MyException {
registry.userDetailsService(testUserDetailsService).passwordEncoder(new TestEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
http.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(testAuthenticationEntryPoint)
.and().sessionManagement().sessionFixation().migrateSession().maximumSessions(-1).sessionRegistry(sessionRegistry()).and()
.and()
.authorizeRequests()
.antMatchers("/").access("hasAuthority('TEST')")
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(testSimpleUrlAuthenticationSuccessHandler)
.failureHandler(testSimpleUrlAuthenticationFailureHandler)
.and()
.logout().logoutSuccessHandler(logoutSuccessHandler)
.and()
.headers().cacheControl().disable().frameOptions().sameOrigin();
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/resources/**");
}
}
Are any ideas why this behaviour is happened?
I'm slowly moving into understanding Spring Cloud Security. I've created an authorization service and it works when authorizing and returning a token, but doesn't return any current user details when using that token, when getting these from OAuth2Authentication. This two lines return an NPE:
userInfo.put("user", user.getUserAuthentication().getPrincipal());
userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
OAuth2Authentication user isn't instantiated and is null, while I understood that it should be by default instantiated by Spring Security. Maybe I am missing some configuration beans? Thanks in advance!
Application.class
#SpringBootApplication
#RestController
#EnableResourceServer
#EnableAuthorizationServer
public class AuthorizationServiceApplication {
#RequestMapping(value = {"/user"}, produces = "application/json")
public Map <String, Object> user (OAuth2Authentication user) {
Map <String, Object> userInfo = new HashMap <>();
userInfo.put("user", user.getUserAuthentication().getPrincipal());
userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
return userInfo;
}
public static void main (String[] args) {
SpringApplication.run(AuthorizationServiceApplication.class, args);
}
}
OAuth2Config.class
#Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Value("${token.secret}")
private String secret;
private AuthenticationManager authenticationManager;
private UserDetailsService userDetailsService;
public OAuth2Config (AuthenticationManager authenticationManager, UserDetailsService userDetailsService) {
this.authenticationManager = authenticationManager;
this.userDetailsService = userDetailsService;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("eagleeye")
.secret(secret)
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
WebSecurityConfigurer.class
#Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean () throws Exception {
return super.authenticationManagerBean();
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
// TODO: implemented DB stuff
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.inMemoryAuthentication()
.withUser("deniss").password("deniss1").roles("USER")
.and()
.withUser("oksana").password("oksana").roles("USER, ADMIN");
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setSessionAttributeName("_csrf");
return repository;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(csrfTokenRepository());
}
}
In the end I got it working like this:
Application.class
#SpringBootApplication
#RestController
#EnableResourceServer
public class AuthorizationServiceApplication {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping("/user")
public Principal user(Principal user) {
log.info("User information display for User: " + user.getName());
return user;
}
#Bean
UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("deniss").password("deniss").roles("USER").build());
return manager;
}
public static void main (String[] args) {
SpringApplication.run(AuthorizationServiceApplication.class, args);
}
}
OAuth2Config.java
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
//TODO: refactor to recieve this info from config server
#Value("${token.secret}")
private String secret;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("eagleeye")
.secret(secret)
.authorizedGrantTypes("refresh_token", "password", "client_credentials")
.scopes("webclient", "mobileclient");
}
}
SecurityConfigurer.class
#Configuration
#EnableGlobalAuthentication
public class SecurityConfigurer extends GlobalAuthenticationConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
// TODO: implemented DB stuff
#Override
public void init(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(this.userDetailsService);
}
}
I met the same problem, it seems a bug of new version. I changed Spring Boot 1.5.9.RELEASEļ¼Spring Cloud Edgware.RELEASE backed to Spring Boot 1.4.4.RELEASEļ¼Spring Cloud Camden.SR5, the problem disappeared.
Set security.oauth2.resource.filter-order=3 configuration property to restore the ordering used in previous versions. See enter link description here for more details.
I've done a very simple demo app to try testing of Spring Boot security.
This is my App configuration
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#SpringBootApplication
public class DemoApplication extends WebSecurityConfigurerAdapter {
#Autowired
private SecurityService securityService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(securityService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated();
http.httpBasic();
http.csrf().disable();
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
My UserDetailsService implementation accepts all users with password 'password' granted admin role to the 'admin' user.
#Service
public class SecurityService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Collection<GrantedAuthority> authorities;
if (username.equals("admin")) {
authorities = Arrays.asList(() -> "ROLE_ADMIN", () -> "ROLE_BASIC");
} else {
authorities = Arrays.asList(() -> "ROLE_BASIC");
}
return new User(username, "password", authorities);
}
}
And I finally created a simple test to check it:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = DemoApplication.class)
#WebAppConfiguration
public class DemoApplicationTests {
#Autowired
private AuthenticationManager authenticationManager;
#Test
public void thatAuthManagerUsesMyService() {
Authentication auth = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken("admin", "password")
);
assertTrue(auth.isAuthenticated());
}
}
I expected the test to pass, but I got a BadCredentialsException instead. After debugging I realized that the AuthenticationManager injected by Spring in the test is not the one I configured. While digging the object in the eclipse debugger I saw that the UserDetailsServer was an InMemoryUserDetailsManager.
I also checked that the configure() methods in DemoApplication are called. What am I doing wrong?
Per WebSecurityConfigurerAdapter api reference for authenticationManagerBean()
Override this method to expose the
AuthenticationManager from configure(AuthenticationManagerBuilder) to
be exposed as a Bean.
So just override authenticationManagerBean() in your WebSecurityConfigurerAdapter and expose it as a bean with #Bean.
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
I'm following the basic Spring Boot OAuth2 example from Dave Syer: https://github.com/dsyer/sparklr-boot/blob/master/src/main/java/demo/Application.java
#Configuration
#ComponentScan
#EnableAutoConfiguration
#RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#RequestMapping("/")
public String home() {
return "Hello World";
}
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
// Just for laughs, apply OAuth protection to only 2 resources
.requestMatchers().antMatchers("/","/admin/beans").and()
.authorizeRequests()
.anyRequest().access("#oauth2.hasScope('read')");
// #formatter:on
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("sparklr");
}
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds("sparklr")
.accessTokenValiditySeconds(60)
.and()
.withClient("my-client-with-registered-redirect")
.authorizedGrantTypes("authorization_code")
.authorities("ROLE_CLIENT")
.scopes("read", "trust")
.resourceIds("sparklr")
.redirectUris("http://anywhere?key=value")
.and()
.withClient("my-client-with-secret")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT")
.scopes("read")
.resourceIds("sparklr")
.secret("secret");
// #formatter:on
}
}
}
The example works very well for both types of grants, but the password grant uses the Spring Boot default security user (the one that echo's out "Using default security password: 927ca0a0-634a-4671-bd1c-1323a866618a" during startup).
My question is how do you override the default user account and actually rely on a WebSecurityConfig? I've added a section like this:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder)
throws Exception {
authManagerBuilder.inMemoryAuthentication().withUser("user")
.password("password").roles("USER");
}
}
But it does not seem to override the default Spring user/password even though the documentation suggests that it should.
What am I missing to get this working?
As I'm still on 2.0.3, I tried a few more things and this appears to be working:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder
.inMemoryAuthentication()
.withUser("user1").password("password1").roles("USER").and()
.withUser("admin1").password("password1").roles("ADMIN");
}
#Bean
#Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
By explicitly defining the authenticationManager bean, the built-in user authentication went away and it started relying on my own inMemoryAuthentication. When 2.0.4 is released, I'll re-evaluate the solution that Dave posted above as it looks like it will be more elegant.
#Configuration
protected static class AuthenticationManagerConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("min").password("min").roles("USER");
}
}
On user authentication i need to retrieve his remote address and remote host.
I'm trying to implement a custom filter to support this, but i'm getting "authenticationManager must be specified".
Another doubt is... What is the correct way to register a custom filter using programmatically ?
Configuration using annotations:
#Configuration
#EnableWebSecurity
public class SecurityApplicationConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private SCAAuthenticationFilter scaAuthenticationFilter;
#Autowired
private SCAAuthenticationProvider scaAuthenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(scaAuthenticationProvider);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilter(scaAuthenticationFilter) // What is the right way ?
.addFilterBefore(scaAuthenticationFilter, AbstractAuthenticationProcessingFilter.class) // What is the right way ?
.csrf().disable()
.authorizeRequests()
.antMatchers("/manual/**").authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.failureUrl("/login?error=true")
.defaultSuccessUrl("/manual")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll()
.and();
}
}
The custom filter:
#Component
public class SCAAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
String remoteHost = request.getRemoteHost();
String remoteAddr = request.getRemoteAddr();
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
SCAAuthenticationToken scaAuthenticationToken = new SCAAuthenticationToken(username, password, remoteHost, remoteAddr);
setDetails(request, scaAuthenticationToken);
return getAuthenticationManager().authenticate(scaAuthenticationToken);
}
}
You need set a authenticationManagerBean for your extended filter and config it corr
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public ExUsernamePasswordAuthenticationFilter exUsernamePasswordAuthenticationFilter()
throws Exception {
ExUsernamePasswordAuthenticationFilter exUsernamePasswordAuthenticationFilter = new ExUsernamePasswordAuthenticationFilter();
exUsernamePasswordAuthenticationFilter
.setAuthenticationManager(authenticationManagerBean());
return exUsernamePasswordAuthenticationFilter;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
RequestMatcher requestMatcher = new RequestMatcher() {
#Override
public boolean matches(HttpServletRequest httpServletRequest) {
if (httpServletRequest.getRequestURI().indexOf("/api", 0) >= 0) {
return true;
}
return false;
}
};
http
.addFilterBefore(exUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
...
}
}
Your custom filter extends Spring Security's UsernamePasswordAuthenticationFilter, which means it needs a reference to the authentication manager. I would create your filter as an #Bean in the security configuration, then follow this answer which explains different options for getting a reference to the AuthenticationManager.
In the class that is extending WebSecurityConfigurerAdapter, override the authenticationManagerBean() method and annotate it with #Bean as such:
#Configuration
#EnableWebMvcSecurity
public class YourCustomConfig extends WebSecurityConfigurerAdapter{
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Now you will be able to #Autowire the AuthenticationManager in other classes.
Another option is to create a configurer for your filter and delegate all the work concerning filter initialization to it (the same way UsernamePasswordAuthenticationFilter is configured through the FormLoginConfigurer and AbstractAuthenticationProcessingFilter is configured by the AbstractAuthenticationFilterConfigurer).
public class SCAAuthenticationConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>() {
public static SCAAuthenticationConfigurer scaAuthentication() {
return new SCAAuthenticationConfigurer()
}
#Override
public void configure(HttpSecurity http) throws Exception {
SCAAuthenticationFilter filter = new SCAAuthenticationFilter()
filter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
// add postProcess(filter) call if require to autowire some fields
http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
}
}
Having such configurer your SecurityConfig will be looking more tidy:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.apply(scaAuthentication())
.and()
// do the rest of configuration
}
}
You may even delegate filter initialization to the ApplicationContext (for example, if you have configuration to inject):
public class FilterWithSettingsConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>() {
#Configuration
#EnableConfigurationProperties(SomeSettings.class)
private static class Config {}
#Override
public void configure(HttpSecurity http) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()
context.parent = http.getSharedObject(ApplicationContext.class)
context.register(Config.class)
context.refresh()
FilterWithSettings filter =
context.getAutowireCapableBeanFactory().createBean(FilterWithSettings.class)
filter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
}
}
For the comprehensive example take a look at the https://github.com/shiver-me-timbers/smt-spring-security-parent/blob/master/smt-spring-security-jwt/src/main/java/shiver/me/timbers/spring/security/JwtSpringSecurityAdaptor.java