Trying to do simple spring boot security test.
I can pass the test only with deprecated NoOpPasswordEncoder in
globalConfigure() method in SpringSecurityConfig.
it works fine, but is it possible get rid of deprecated NoOpPasswordEncoder?
#Configuration
#EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().fullyAuthenticated()
.antMatchers("/").permitAll()
.and()
.formLogin().defaultSuccessUrl("/", true)
.loginPage("/login").permitAll().and().logout().permitAll()
.and().httpBasic();
}
#SuppressWarnings("deprecated")
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authBuilder) throws Exception{
authBuilder.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("user").password("user").roles("USER")
.and()
.withUser("admin").password("admin").roles("USER", "ADMIN");
}
}
Testing spring security
#RunWith(SpringJUnit4ClassRunner.class)
#AutoConfigureMockMvc
#SpringBootTest
public class SpringSecurityConfigTest {
#Autowired
MockMvc mockMvc;
#Test
public void userIsAuthenticatedTest() throws Exception {
mockMvc.perform(formLogin().user("admin").password("admin"))
.andExpect(authenticated());
}
}
It depends what you want to do exactly. If you just want your test to pass and get rid of the deprecation you can remove the password encoder and add the {noop} prefix to the passwords in your configureGlobal method:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authBuilder) throws Exception {
authBuilder.inMemoryAuthentication()
.withUser("user").password("{noop}user").roles("USER")
.and()
.withUser("admin").password("{noop}admin").roles("USER", "ADMIN");
}
Spring Security 5 changed the default password encoder to Delegating Password Encoder which uses the prefix in curly braces to determine which password encoder to use, s. https://docs.spring.io/spring-security/site/docs/5.0.5.RELEASE/reference/htmlsingle/#pe-dpe-format
However, if you want to use this security config in production you should probably use a different encoder anyway.
Related
I am trying to setup OAuth2 login in spring boot, every thing is working good in my dev environment, however when I deploy it on production behind the Apache Http server using proxy configuration, authentication fails on Invalid Redirect URI check.
As suggested in some other post I already tried
1. use-forward-headers: true
2. Rewriting the request redirect URI
with no luck.
So I decided to hook my own custom OAuth2LoginAuthenticationProvider so that I can override this check.
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Value("${baseUrl}")
private String domainPath;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(new MyCustomFilter(domainPath), OAuth2LoginAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated().and()
.logout().logoutSuccessUrl("/")
.and()
.oauth2Login().userInfoEndpoint().customUserType(CmmOAuth2User.class, "custom");
}
#Bean
public MyOAuth2AuthenticationProvider authenticationProvider() {
MyOAuth2AuthenticationProvider authProvider
= new MyOAuth2AuthenticationProvider();
return authProvider;
}
#Autowired
public void configureProviderManager(ProviderManager providerManager) {
providerManager.getProviders().add(0,authenticationProvider());
}
#Bean
public ProviderManager authenticationManager() {
List<AuthenticationProvider> authProviderList = new ArrayList<AuthenticationProvider>();
authProviderList.add(0,authenticationProvider());
ProviderManager providerManager = new ProviderManager(authProviderList);
return providerManager;
}
}
My custom MyOAuth2AuthenticationProvider is not being called.
Is it possible to have an auth server defining all four types of grant types?
And if yes then how can I test them? A sample auth client for grant type password would be helpful.
I am presuming that you want to implement it using Spring OAuth server implementation. One example by Dave Syer is hosted on GitHub, and it uses JDBC as the token store.
Now for the grant types, you have the option to configure that per client that you register with your OAuth server. You will notice that it has been setup using "authorizedGrantTypes" in the example below.
For testing - password grant type is very easy to test using a REST client like Postman and making a call to the OAuth server.
Feel free to comment on this if you need more help. All the best!
Resource Server
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.tokenStore(tokenStore);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
Authorization Server
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager auth;
#Autowired
private DataSource dataSource;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#Bean
public JdbcTokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security.passwordEncoder(passwordEncoder);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authorizationCodeServices(authorizationCodeServices())
.authenticationManager(auth).tokenStore(tokenStore())
.approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.jdbc(dataSource)
.passwordEncoder(passwordEncoder)
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code",
"refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(60).and()
.withClient("my-client-with-registered-redirect")
.authorizedGrantTypes("authorization_code")
.authorities("ROLE_CLIENT").scopes("read", "trust")
.resourceIds("oauth2-resource")
.redirectUris("http://anywhere?key=value").and()
.withClient("my-client-with-secret")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT").scopes("read")
.resourceIds("oauth2-resource").secret("secret");
// #formatter:on
}
}
we are currently trying to upgrade from Camel 2.19.0 to 2.20.0.
We have one timed routed which schedule autostart = false
#ConfigurationProperties
#Component
public class AdaptionService extends SpringRouteBuilder {
#Value("${control.timer.cron}")
private String timerCron;
#Override
public void configure() throws Exception {
from("quartz2://adaptionServices/UserUpdateTimer?cron=" + timerCron)
.routeId("scheduler").autoStartup(false)
.to("direct:route-userUpdate");
from("direct:route-userUpdate")
.routeId("adaption_service")
.log("Executing Adaption Service (timed)");
}
No in the CamelContextConfiguration I try to star the timer route (I know I could simply achieve this by autoStartup(true). It is just an example to abstract from more complex use case we have.
#Configuration
public class CamelApplicationContextConfiguration implements CamelContextConfiguration {
#Override
public void beforeApplicationStart(CamelContext camelContext) {
camelContext.setUseMDCLogging(true);
}
#Override
public void afterApplicationStart(CamelContext camelContext) {
try {
camelContext.startRoute("scheduler");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
That does not work. I checked startRoute(..) and appearently the routeService inside the camelContext is completely empty. With 2.19.0 this works without a flaw.
Any suggestions?
Regards
Holger
The Camel 2.20 release has changed the startup logic when using Spring / Spring-Boot with Camel. You can see that in the release notes.
However we should make CamelContextConfiguration call the afterApplicationStart at a later stage. And also document this API a bit better when its called:
I have logged a ticket about this: https://issues.apache.org/jira/browse/CAMEL-11945
I'm having two routes in two separated projects :
First route is setting the header with a data format bean name as a constant :
setHeader("dataFormatBeanName", constant("myFirstList"))
First route :
public class MyTest {
#Configuration
public static class MyTestConfig extends CamelConfiguration {
#Bean(name = "myFirstList")
public DataFormat getMyFirstListDataFormat() {
return new MyFirstListDataFormat();
}
#Bean(name = "mySecondList")
public DataFormat getMySecondListDataFormat() {
return new MySecondListDataFormat();
}
#Bean
public RouteBuilder route() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:testFirstDataFormat").setHeader("dataFormatBeanName", constant("myFirstList")).to("direct:myRoute");
from("direct:testSecondDataFormat").setHeader("dataFormatBeanName", constant("mySecondList")).to("direct:myRoute");
}
};
}
}
}
Second route is supposed to retrieve the bean name from the header and use it as a custom marshaller. Something like :
custom(header("dataFormatBeanName"))
(doesn't compile)
Anyone knows how I'm supposed to get my bean name from the header to use it in the custom method ?
#Component
public class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
final RouteDefinition routedefinition = this.from("direct:myRoute");
routedefinition.marshal().custom(??????????).to("netty4:tcp://{{route.address}}:{{port}}?textline=true&sync=true");
}
After a few more hours searching, here is the solution a found :
No changes in the first class.
Second class uses an anonymous DataFormat in which I retrieve the bean name from the header and get the spring bean from camel context before calling its marshal method.
The AbstractXxxDataFormat class belongs to project2 and is inherited by the Project1 DataFormat.
#Override
public void configure() throws Exception {
final RouteDefinition routedefinition = this.from("direct:myRoute");
routedefinition.marshal(new DataFormat() {
#Override
public void marshal(final Exchange exchange, final Object graph, final OutputStream stream) throws Exception {
AbstractXxxDataFormat myDataFormat = (AbstractGoalDataFormat) getContext().getRegistry().lookupByName(exchange.getIn().getHeader("dataFormatBeanName", String.class));
myDataFormat.marshal(exchange, graph, stream);
}
#Override
public Object unmarshal(final Exchange exchange, final InputStream stream) throws Exception {
return null;
}
});
routedefinition.to("netty4:tcp://{{route.address}}:{{port}}?textline=true&sync=true");
}
If there's any better solution available, I'll be interested.
Have you tried simple("${header.dataFormatBeanName}") to access the header?
Also, rather than passing the format bean name in a header in the first place, why not factor out each .marshal() call into two subroutes (one for formatBeanA and one for formatBeanB) and then call the appropriate subroute rather than setting the header in the first place? I believe this could be a cleaner approach.
If you really need to get it in the route as a variable (as opposed to a predicate to be used in the builder api) you could use an inline processor to extract it:
public class MyRouteBuilder extends RouteBuilder {
public void configure() throws Exception {
from("someEndpoint")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
String beanName = exchange.getHeader("beanNameHeader");
}
});
}
}
Just be careful of scope and concurrency when storing the extracted beanName however.
A collegue of mine (thanks to him) found the definite solution :
set bean name in the exchange properties :
exchange.setProperty("myDataFormat", "myDataFormatAutowiredBean");
retrieve the dataFormat bean with RecipientList pattern and (un)marshal :
routedefinition.recipientList(simple("dataformat:${property.myDataFormat}:marshal"));
routedefinition.recipientList(simple("dataformat:${property.myDataFormat}:unmarshal"));
Very concise and works just fine.
Any ideas on this?
From Tomcat:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1011E:(pos 8): Method call: Attempted to call method throwOnError(java.lang.Boolean) on null context object
Returned to Client:
java.lang.IllegalArgumentException: Failed to evaluate expression '#oauth2.throwOnError(#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN'))'
org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:13)
org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:34)
org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:18)
org.springframework.security.access.vote.UnanimousBased.decide(UnanimousBased.java:77)
I do a POST to my authorization server /oauth/token and get a token.
If I take that token and add a Authorization: Bearer header to a GET request to the resource server, I get that error.
In my subclass of ResourceServerConfigurerAdapter, the line it blows up on is here:
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**")
.access("#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN')")
.accessDecisionManager(accessDecisionManager())
.anyRequest()
.fullyAuthenticated();
I know that the resource server recognizes the token because if i leave it out, I get the proper error. If I make up a fake one then I get the "invalid token" message, which is expected. If I use the actual token Spring is jumps in and blows up on the .access()
Thanks in advance for any help. I'm putting the code for my ResourceReserver below:
#Configuration
#EnableWebSecurity
#EnableResourceServer
public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
#Autowired
private OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint;
#Autowired
private ResourceServerTokenServices tokenServices;
#Autowired
private TokenStore tokenStore;
#Autowired
#Qualifier("oauth2ResourceId")
private String oauth2ResourceId;
#Autowired
#Qualifier("oauth2Realm")
private String oauth2Realm;
#Bean
OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint() {
final OAuth2AuthenticationEntryPoint entryPoint = new OAuth2AuthenticationEntryPoint();
entryPoint.setRealmName(oauth2Realm);
entryPoint.setTypeName("Basic");
return entryPoint;
}
private AccessDecisionManager accessDecisionManager() {
return new UnanimousBased(Arrays.<AccessDecisionVoter>asList(new ScopeVoter(),
new AuthenticatedVoter(),
new WebExpressionVoter()));
}
private AuthenticationManager getAuthenticationManager() {
final OAuth2AuthenticationManager oAuth2AuthenticationManager = new OAuth2AuthenticationManager();
oAuth2AuthenticationManager.setTokenServices(tokenServices);
return oAuth2AuthenticationManager;
}
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**")
.access("#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN')")
.accessDecisionManager(accessDecisionManager())
.anyRequest()
.fullyAuthenticated();
http
.anonymous()
.disable();
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
http
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(logoutSuccessHandler())
.invalidateHttpSession(true);
/*
http
.requiresChannel()
.antMatchers("/oauth/api/**")
.requiresSecure();
http
.portMapper()
.http(8080)
.mapsTo(8443);
*/
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.authenticationManager(getAuthenticationManager())
.tokenServices(tokenServices)
.tokenStore(tokenStore)
.resourceId(oauth2ResourceId);
}
private LogoutSuccessHandler logoutSuccessHandler() {
return new OAuth2SuccessLogoutHandler(tokenStore);
}
static final class OAuth2SuccessLogoutHandler implements LogoutSuccessHandler {
private final TokenStore tokenStore;
public OAuth2SuccessLogoutHandler(final TokenStore tokenStore) {
this.tokenStore = tokenStore;
}
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
request.toString();
}
}
}
The hasAnyRole() method is not OAuth2 related and therefore is not on the #oauth2 variable (it's on the root so you don't need to qualify it).