I am using token-based authentication. I have a custom authentication filter which does a REST call to authenticate the user. I managed to create and configure the custom authentication provider but having trouble setting the order of the providers. I want the default DaoAuthenticationProvider to be the default and customProvider to be the secondary.
This is how I configured the customAuthenticationProvider
#Inject
private CustomAuthenticationProvider customAuthenticationProvider;
#Inject
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider)
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
How can I configure customAuthenticationProvider to be the second provider?
PS: I couldn't Inject the customAuthenticationProvider into SecurityConfiguration.java as the Proxy couldn't be created until I added the following scope to customAuthenticationProvider.
#Component("alfrescoAuthenticationProvider")
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
public class AlfrescoAuthenticationProvider implements AuthenticationProvider {
....
}
I don't understand what you said about why you can not inject. My SecurityConfiguration class is as below:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Inject
private Http401UnauthorizedEntryPoint authenticationEntryPoint;
#Inject
private UserDetailsService userDetailsService;
#Inject
private TokenProvider tokenProvider;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Inject
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
auth.authenticationProvider(weixinAuthenticationProvider());
}
#Bean
public WeixinAuthenticationProvider weixinAuthenticationProvider() {
WeixinAuthenticationProvider provider = new WeixinAuthenticationProvider(userDetailsService, passwordEncoder());
return provider;
}
#Override
public void configure(WebSecurity web) throws Exception {
......
I hope it can help.
Related
I am going to test my controller using springsecurity.
#PostMapping("/xx")
public String xx(Authentication auth){
String userId = (String)auth.getPrincipal();
....
}
I have no idea how to mock Authentication object. Or is there other way to deal with it?
My test shown below
public class MyControllerTest{
#Autowired
private XxController xxController;
private MockMvc mockMvc;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(xxController).build();
}
#Test
public void xxtestMethod() throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post(/xx))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
}
For basic auth, after trying different ways to do it, I found this worked for me :
MockMvcRequestBuilders
.post(urlEndpoint)
.principal(new MyAuthentication())
Notice that MyAuthentication class implements the Authentication interface
I have a very special requirements in my Spring Boot web application:
I have internal and external users. Internal users login to the web application by using keycloak authentication (they can work in the web application), but our external users login by simple Spring Boot authentication (what they can do is just to download some files generated by web application)
What I want to do is to have multiple authentication model:
all the path except /download/* to be authenticated by our Keycloak authentication, but the path /download/* to be authenticated by SpringBoot basic authentication.
At the moment I have the following:
#Configuration
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Configuration
#ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
#Order(1)
public static class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.regexMatcher("^(?!.*/download/export/test)")
.authorizeRequests()
.anyRequest().hasAnyRole("ADMIN", "SUPER_ADMIN")
.and()
.logout().logoutSuccessUrl("/bye");
}
}
#Configuration
#Order(2)
public static class DownloadableExportFilesSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/download/export/test")
.authorizeRequests()
.anyRequest().hasRole("USER1")
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password1").roles("USER1");
}
}
}
But it does not work well, because every time the external user wants to download something (/download/export/test), it prompts the login form, but after entering the correct external user username and password, than it prompts the keycloak authentication login form.
I don't get any error just a warning:
2016-06-20 16:31:28.771 WARN 6872 --- [nio-8087-exec-6] o.k.a.s.token.SpringSecurityTokenStore : Expected a KeycloakAuthenticationToken, but found org.springframework.security.authentication.UsernamePasswordAuthenticationToken#3fb541cc: Principal: org.springframework.security.core.userdetails.User#36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER1; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#957e: RemoteIpAddress: 127.0.0.1; SessionId: 4C1BD3EA1FD7F50477548DEC4B5B5162; Granted Authorities: ROLE_USER1
Do you have any ideas?
I experienced some headaches when implementing basic authentication next to Keycloak authentication, because still while doing multiple WebSecurityAdapter implementations 'by the book', the Keycloak authentication filter was called even when basic authentication succeeded.
The reason lies here:
http://www.keycloak.org/docs/latest/securing_apps/index.html#avoid-double-filter-bean-registration
So if you use the Keycloak Spring Security Adapter together with Spring Boot, make sure to add those two beans (in addition to the valid answer by Jacob von Lingen):
#Configuration
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Configuration
#Order(1) //Order is 1 -> First the special case
public static class DownloadableExportFilesSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.antMatcher("/download/export/test")
.authorizeRequests()
.anyRequest().hasRole("USER1")
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password1").roles("USER1");
}
}
#Configuration
#ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
//no Order, will be configured last => All other urls should go through the keycloak adapter
public static class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
// necessary due to http://www.keycloak.org/docs/latest/securing_apps/index.html#avoid-double-filter-bean-registration
#Bean
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(KeycloakAuthenticationProcessingFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
// necessary due to http://www.keycloak.org/docs/latest/securing_apps/index.html#avoid-double-filter-bean-registration
#Bean
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(KeycloakPreAuthActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
super.configure(http);
http
.authorizeRequests()
.anyRequest().hasAnyRole("ADMIN", "SUPER_ADMIN")
.and()
.logout().logoutSuccessUrl("/bye");
}
}
}
The key for multiple HttpSecurity is to register the 'special cases' before the normal one. In other words, the /download/export/test authentication adapter should be registered before the keycloak adapter.
Another important thing to notice, once an authentication is successful, no other adapter is called (so the .regexMatcher("^(?!.*/download/export/test)") is not necessary). More info for Multiple HttpSecurity can be found here.
Below you code with minimal changes:
#Configuration
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Configuration
#Order(1) //Order is 1 -> First the special case
public static class DownloadableExportFilesSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/download/export/test")
.authorizeRequests()
.anyRequest().hasRole("USER1")
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password1").roles("USER1");
}
}
#Configuration
#ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
#Order(2) //Order is 2 -> All other urls should go through the keycloak adapter
public static class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
//removed .regexMatcher("^(?!.*/download/export/test)")
.authorizeRequests()
.anyRequest().hasAnyRole("ADMIN", "SUPER_ADMIN")
.and()
.logout().logoutSuccessUrl("/bye");
}
}
}
In my work is used the following pattern: ManagedBeans always have a single Service. When we need to use a different service in ManagedBean, create a method in Service using the Service needed.
But I was wondering if the most correct would be to have all necessary Services injected directly into ManagedBean because I found it very annoying to have to always create methods in the Services only to call other Services. Also apparently it decreases the cohesion of the Service as it will eventually having methods that should not have.
This is the way used in my work.
#ManagedBean
#ViewScoped
public class UserController {
private User user;
private List<Job> jobs;
#Inject
private UserService userService;
#PostConstruct
public void init() {
users = userService.listAllJobs();
}
public User getUser() {
return user;
}
public List<Job> getJobs() {
return jobs;
}
public void saveUser() {
userService.save(user);
}
}
#Stateless
public class UserService{
#Inject
private JobService jobService;
public List<Job> listAllJobs(){
jobService.listAll();
}
}
This is the way I prefer and also think more correct. Instead of create listAllJobs method in UserService, making it less cohesive, i just injected the JobService into UserController. And that's how I will do with all the services i need for this ManagedBean
#ManagedBean
#ViewScoped
public class UserController {
private User user;
private List<Job> jobs;
#Inject
private UserService userService;
#Inject
private JobService jobService;
#PostConstruct
public void init() {
users = jobService.listAll();
}
public User getUser() {
return user;
}
public List<Job> getJobs() {
return jobs;
}
public void saveUser() {
userService.save(user);
}
}
i want to use in second way in my work but dont know if is the best way. Please tell me whats the best and the advantages
A POST request
http://localhost:9278/submitEnrollment
to a Spring Boot application that encapsulates an external SOAP call results in the following:
{
"timestamp": 1439480941381,
"status": 401,
"error": "Unauthorized",
"message": "Full authentication is required to access this resource",
"path": "/submitEnrollment"
}
This doesn't seem to be a normal behavior, I'm wondering what Spring Boot configurations I need to relax/disable to prevent this client authentication.
Here are relevant pieces of code:
Configuration for the app (that entails all the necessary plumbing to send a secured SOAP call over SSL and should affect web tier):
#Configuration
#ComponentScan({"a.b.c.d", "com.submit.enrollment"})
#PropertySource("classpath:/submit-enrollment.properties")
public class SubmitEnrollmentConfig {
#Value("${marshaller.contextPaths}")
private String[] marshallerContextPaths;
#Value("${default.Uri}")
private String defaultUri;
#Bean
public FfmSoapClient connectivityClient() throws Throwable {
FfmSoapClient client = new FfmSoapClient();
client.setWebServiceTemplate(webServiceTemplate());
return client;
}
#Bean
public KeyStore keyStore() throws Throwable {
KeyStoreFactoryBean keyStoreFactory = new KeyStoreFactoryBean();
keyStoreFactory.setPassword("!zxy!36!");
keyStoreFactory.setLocation(new ClassPathResource("zxy.jks"));
keyStoreFactory.setType("jks");
keyStoreFactory.afterPropertiesSet();
return keyStoreFactory.getObject();
}
#Bean
public KeyManager[] keyManagers() throws Throwable{
KeyManagersFactoryBean keyManagerFactory = new KeyManagersFactoryBean();
keyManagerFactory.setKeyStore(keyStore());
keyManagerFactory.setPassword("!zxy!36!");
keyManagerFactory.afterPropertiesSet();
return keyManagerFactory.getObject();
}
#Bean
public HttpsUrlConnectionMessageSender httpsUrlSender() throws Throwable {
HttpsUrlConnectionMessageSender sender = new HttpsUrlConnectionMessageSender();
sender.setSslProtocol("TLS");
sender.setKeyManagers(keyManagers());
return sender;
}
#Bean
public WebServiceTemplate webServiceTemplate() throws Throwable {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMarshaller(marshaller());
webServiceTemplate.setUnmarshaller(marshaller());
webServiceTemplate.setDefaultUri(defaultUri);
webServiceTemplate.setMessageFactory(messageFactory());
webServiceTemplate.setMessageSender(/*new HttpComponentsMessageSender()*/httpsUrlSender());
webServiceTemplate.setInterceptors(new ClientInterceptor[] { wss4jSecurityInterceptor(), new LogbackInterceptor() }); //order matters
webServiceTemplate.setMessageSender(httpsUrlSender());
return webServiceTemplate;
}
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPaths(marshallerContextPaths);
return marshaller;
}
#Bean
public SaajSoapMessageFactory messageFactory() {
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory();
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
return messageFactory;
}
#Bean
public Wss4jSecurityInterceptor wss4jSecurityInterceptor() throws Throwable{
Wss4jSecurityInterceptor wss4jSecurityInterceptor = new Wss4jSecurityInterceptor();
wss4jSecurityInterceptor.setSecurementActions(/*"UsernameToken"*/WSHandlerConstants.USERNAME_TOKEN + " "+ WSHandlerConstants.TIMESTAMP);
//wss4jSecurityInterceptor.setSecurementActions("Signature");
wss4jSecurityInterceptor.setSecurementUsername("07.ZIP.NJ*.390.639");
wss4jSecurityInterceptor.setSecurementPassword("oLD#cDh$(dKnCM");
wss4jSecurityInterceptor.setSecurementPasswordType(/*"PasswordDigest"*/WSConstants.PW_DIGEST);
wss4jSecurityInterceptor.setSecurementEncryptionCrypto(crypto());
wss4jSecurityInterceptor.setSecurementEncryptionKeyIdentifier("DirectReference");
//wss4jSecurityInterceptor.setValidationActions("Signature");
//wss4jSecurityInterceptor.setValidationSignatureCrypto( crypto() );
wss4jSecurityInterceptor.setSecurementTimeToLive(300);
return wss4jSecurityInterceptor;
}
#Bean
public Crypto crypto() throws Throwable {
CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean();
cryptoFactoryBean.setKeyStoreLocation(new ClassPathResource("zipari.jks"));
cryptoFactoryBean.setKeyStorePassword("!zxy!36!");
cryptoFactoryBean.afterPropertiesSet();
Crypto crypto = cryptoFactoryBean.getObject();
System.out.println("created crypto store: "+ crypto);
return crypto;
}
#Configuration
static class DatabaseConfig {
#Bean #Lazy
DataSource dataSource() {
return null;
}
}
}
Application:
public static void main(String[] args) throws Throwable {
SpringApplication app = new SpringApplication(SubmitEnrollmentApplication.class);
//app.addListeners(new ApplicationPidFileWriter());
ApplicationContext ctx = app.run(args);
Controller:
#RestController
public class SubmitEnrollmentController {
private final Logger logger = LoggerFactory.getLogger(SubmitEnrollmentController.class);
//#Autowired #Qualifier("brokerService")private BrokerService service;
#RequestMapping(value = "/submitEnrollment", method = RequestMethod.POST, consumes="application/json")
public String submitEnrollment(#RequestBody String jsonIn){
logger.info("Received submit enrollment request: {}, start processing...", jsonIn);
The following addition to the main Spring config file helped me achieve what I needed:
#Configuration
static class WebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/**");
}
}
Your problem is, your rest endpoints are authenticated with spring security. So the error message clearly indicates that, you want to be authenticate yourself before sending the request. You can ignore the authentication, until you make sure everything is working. What you will need is something like this.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/submitEnrollment").permitAll().and().csrf().disable();
}
You can find a good config from here. If you need more complex config, go through this jhipster project, and specifically this file.
It is better you can go through these docs as well. Hope this helps.
I am using picketlink to authenticate a user on project. I also created a #produces annotated method, so I would be able to inject the authenticated user in other places. Now, I am using envers and besides the default information, I would like to store the user that performed the action, but I cannot inject it in the envers listener. It is always null. How can I make this injection, or retrieve this information?
The producer class:
#SessionScoped
public class Resources implements Serializable {
private static final long serialVersionUID = 1L;
#EJB
private AuthenticationManagerBean authenticator;
#Inject
private Identity credentials;
#CurrentUser
private AuthenticatedUser currentUser;
#Produces
#CurrentUser
#SessionScoped
private AuthenticatedUser createAuthenticatedUser() {
AuthenticatedUser user = new AuthenticatedUser();
org.picketlink.idm.model.basic.User loggedInUser = (org.picketlink.idm.model.basic.User) credentials.getAccount();
User pu = authenticator.getUserRoles(loggedInUser.getLoginName());
if (pu != null) {
user.setUser(pu.getName());
for (Role role : pu.getRoles()) {
user.getRoles().add(role.getName());
}
}
return user;
}
#Produces
public Logger produceLog(InjectionPoint injectionPoint) {
return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
and the envers listener:
public class AuditListener implements RevisionListener, Serializable {
private static final long serialVersionUID = 1L;
#Inject
#CurrentUser
private AuthenticatedUser identity; //this is always null
public void newRevision(Object revisionEntity) {
System.out.println(identity.getUser());
}
}
I had a similiar problem. The injection does not work because RevisionListener is not managed by CDI. That way, you have to lookup for the bean yourself. This is the way you could do it:
public AuthenticatedUser getAuthenticatedUser() {
BeanManager beanManager = (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
Bean<AuthenticatedUser> bean = (Bean<AuthenticatedUser>) beanManager.getBeans(AuthenticatedUser.class, new AnnotationLiteral<CurrentUser>() {
}).iterator().next();
CreationalContext<AuthenticatedUser> ctx = beanManager.createCreationalContext(bean);
return (AuthenticatedUser) beanManager.getReference(bean, AuthenticatedUser.class, ctx);
}