Custom PasswordEncoder - spring-security

I need to create a custom password encoder. I have completed to following tasks :
applicationContext-Security.xml
<authentication-manager alias="authenticationManager">
<authentication-provider>
<password-encoder ref="AppPasswordEncoder" />
<jdbc-user-service data-source-ref="dataSource" authorities-by-username-query="select username,password from username where username=?"/>
</authentication-provider>
<beans:bean class="com.app.security.MyPasswordEncoder" id="AppPasswordEncoder"/>
</authentication-manager>
class
public class SnatiPasswordEncoder implements PasswordEncoder {
#Override
public String encodePassword(String arg0, Object arg1)
throws DataAccessException {
return null;
}
#Override
public boolean isPasswordValid(String arg0, String arg1, Object arg2)
throws DataAccessException {
return false;
}
}
Several steps to encode the password :
ISO-8859-1
md5
base64
What should be my next step ?

Related

How implement UserDetailsService for several types of users?

In my web app when I have one type of user (typical_user) I do the following:
1) Implement UserDetailsService
public class UserServiceImpl implements UserService, UserDetailsService {
private UserDao userDao;
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException
{
UserEntity user = userDao.loadUserByEmail(username);
if (user == null) {
throw new UsernameNotFoundException(String.format(
getMessageBundle().getString("badCredentials"), username));
}
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
User userDetails = new User(user.getEmail(), user.getPassword(),
authorities);
return userDetails;
}}
2) Write configuration for that user in security-config.xml like this:
<security:authentication-manager>
<security:authentication-provider
user-service-ref="userService">
<security:password-encoder hash="md5" />
</security:authentication-provider>
</security:authentication-manager>
<bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userService" />
<property name="hideUserNotFoundExceptions" value="false" />
</bean>
But now I want to have another type of user (admin). So, I need another implementation of loadUserByUsername method (where user will get ROLE_ADMIN).
I can write another class (AdminServiceImpl) but how my security-config.xml will look like??
As suggested, switch to database storage. Assuming you're using an ORM for DB management:
public class Role implements org.springframework.security.core.GrantedAuthority {
// implements what must be implemented
}
public class User implements org.springframework.security.core.userdetails.UserDetails {
// your stuff...
#ManyToMany(fetch = FetchType.EAGER) // shouldn't be a problem here to fetch eagerly
private Collection<Role> roles = new HashSet<Role>();
// add getters and setters
/**
* #see org.springframework.security.core.userdetails.UserDetails#getAuthorities()
*/
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles();
}
}
public class UserDetailsServiceImpl implements
org.springframework.security.core.userdetails.UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// Load the user from your database. The ORM will take care of loading his Role collection.
}
}

how to delete refreshtoken and access token when user logout oauth 2.0?

i tried with ....
<sec:logout invalidate-session="true" logout-success-url="/logoutsuccess" logouturl="/logout/>
but it is not working properly....
i want to clear everything like refresh token and access token session , cookies when user logout....
my security-servlet.xml looks like this
<!-- Protected resources -->
<sec:http create-session="never" entry-point-ref="oauthAuthenticationEntryPoint"
access-decision-manager-ref="accessDecisionManager"
xmlns="http://www.springframework.org/schema/security">
<sec:anonymous enabled="false" />
<sec:intercept-url pattern="/data/user/*"
access="IS_AUTHENTICATED_FULLY" />
<sec:logout delete-cookies="JSESSIONID" invalidate-session="true" />
<sec:custom-filter ref="resourceServerFilter"
before="PRE_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
</sec:http>
In Spring-boot application I will:
1. get OAuth2AccessToken
2. using it will delete OAuth2RefreshToken
3. and then delete itself
#Component
public class CustomLogoutSuccessHandler
extends AbstractAuthenticationTargetUrlRequestHandler
implements LogoutSuccessHandler {
private static final String BEARER_AUTHENTICATION = "Bearer ";
private static final String HEADER_AUTHORIZATION = "authorization";
#Autowired
private TokenStore tokenStore;
#Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Authentication authentication) throws IOException, ServletException {
String token = httpServletRequest.getHeader(HEADER_AUTHORIZATION);
if (token != null && token.startsWith(BEARER_AUTHENTICATION)) {
String accessTokenValue = token.split(" ")[1];
OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessTokenValue);
if (oAuth2AccessToken != null) {
OAuth2RefreshToken oAuth2RefreshToken = oAuth2AccessToken.getRefreshToken();
if (oAuth2RefreshToken != null)
tokenStore.removeRefreshToken(oAuth2RefreshToken);
tokenStore.removeAccessToken(oAuth2AccessToken);
}
}
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
}
}
you can do these things into sessionDestroyedListener...almost look like this..
In this code i am updating lastLogout date ..you can do what you want
#Component("sessionDestroyedEventListener")
public class SessionDestroyedEventListener implements ApplicationListener<SessionDestroyedEvent>{
// private static Logger logger = BaseLogger.getLogger(AuthenticationEventListener.class);
#Autowired
private AuthenticationService authenticationService;
public void setAuthenticationService(AuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
/**
* Capture sessionDestroyed event and update lastLogout date after session destroyed of particular user.
*/
#Override
public void onApplicationEvent(SessionDestroyedEvent appEvent) {
SessionDestroyedEvent event = (SessionDestroyedEvent) appEvent;
Object obj = null;
UserInfo userInfo = null;
ArrayList<SecurityContext> sc = (ArrayList<SecurityContext>) event.getSecurityContexts();
Iterator<SecurityContext> itr = sc.iterator();
while (itr.hasNext()) {
obj = itr.next().getAuthentication().getPrincipal();
if (obj instanceof UserInfo) {
userInfo = (UserInfo) obj;
} else {
String userCode = (String) obj;
if (userCode == null || "".equals(userCode)) {
userCode = "UnDefinedUser";
}
userInfo = new UserInfo(userCode);
}
//authenticationService.updateLastLogoutDate(userInfo.getUsername());
}
}
}

Ways to toggle spring security SAML on and off

I have a pretty standard implementation of spring security saml into my application in addition to other authentication mechanisms. Out of the box SAML will not be configured but can be configured through a form, so by default SAML should be disabled. I'd like to easily be able to toggle SAML on / off but am not sure what the best way to do this would be.
It seems like one approach would be to do a custom FilterChainProxy where if I check if saml is enabled and if so to ignore the samlFilter chain(How to delete one filter from default filter stack in Spring Security?) and also do a similar implementation for the Metadata Generator Filter.
Any advice would be great.
Here is my config:
<http auto-config="false" use-expressions="true"
access-decision-manager-ref="webAccessDecisionManager"
disable-url-rewriting="false"
create-session="never"
authentication-manager-ref="authenticationManager">
<custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</http>
Metadata Generator Filter:
<beans:bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<beans:constructor-arg>
<beans:bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<beans:property name="entityId" value="${saml.entityId}"/>
<beans:property name="signMetadata" value="${saml.signMetadata}"/>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
Saml Filter:
<beans:bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<filter-chain-map request-matcher="ant">
<filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
<filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
<filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/>
<filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
<filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
<filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
<filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/>
</filter-chain-map>
</beans:bean>
EDIT: Here is my implementation, it is a bit hackish and relies on a deprecated method but it works
The below snippet disables MetadataGeneratorFilter:
public class MyMetadataGeneratorFilter extends MetadataGeneratorFilter {
private boolean isActive = false;
public MyMetadataGeneratorFilter(MetadataGenerator generator) {
super(generator);
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (isActive) {
processMetadataInitialization((HttpServletRequest) request);
}
chain.doFilter(request, response);
}
public void setActive(boolean active) {
isActive = active;
}
}
There is also the samlFilter / FilterChainMap which is autowired. If saml is enabled, I leave this chain as is, if it is disabled, I set the chain to an empty map in my service which enables / disables saml.
Upon initialization, I get the filterchainmap values:
private Map<RequestMatcher, List<Filter>> map;
#Override
public void init() throws ServiceException, MetadataProviderException {
SamlConfig samlConfig = getConfig();
map = samlFilter.getFilterChainMap();
applySamlConfig(samlConfig);
}
In the below method, I set the filter chain map to either the original map provided in the spring xml(if enabled) or an empty map (if disabled).
public void applySamlConfig(SamlConfig samlConfig) throws ServiceException, MetadataProviderException {
if (!samlConfig.isEnabled()) {
Map<RequestMatcher, List<Filter>> emptyMap = samlFilter.getFilterChainMap();
emptyMap.clear();
samlFilter.setFilterChainMap(emptyMap);
return;
}
samlFilter.setFilterChainMap(map);
i added a custom filter in the entry-point-ref definition. This filter skips all following filters if the feature is not enabled.
<security:http entry-point-ref="samlEntryPoint">
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<!-- This filter checks if the SSO-Feature is enabled - otherwise all following security filters will be skipped -->
<security:custom-filter before="BASIC_AUTH_FILTER" ref="ssoEnabledFilter"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter" />
<security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter" />
The ssoEnabledFilter:
public class SsoEnabledFilter extends GenericFilterBean {
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) throws IOException, ServletException {
boolean ssoEnabled = isSsoEnabled();
if (ssoEnabled) {
filterChain.doFilter(request, response);
} else {
request.getRequestDispatcher(((HttpServletRequest) request).getServletPath()).forward(request, response);
}
}
}
So far I've been implementing this using a custom Spring namespace which includes or skips certain beans based on the backend configuration and reloading of the Spring context in case the backend configuration changes.
Edit : fixed error signaled by TheTurkish
If you want to be able to switch the use of SAML on a running application, the simpler would be to use a wrapper around samlFilter. For example
public class FilterWrapper extends GenericFilterBean {
private Filter inner;
private boolean active;
private boolean targetFilterLifeCycle = false;
public Filter getInner() {
return inner;
}
public void setInner(Filter inner) {
this.inner = inner;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
#Override
public void doFilter(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException {
if (active) {
inner.doFilter(sr, sr1, fc);
}
else {
fc.doFilter(str,sr1);
}
}
#Override
protected void initFilterBean() throws ServletException {
super.initFilterBean();
if (inner == null) {
throw new ServletException("Inner cannot be null");
}
if (targetFilterLifeCycle) {
inner.init(getFilterConfig());
}
}
#Override
public void destroy() {
super.destroy();
if (inner != null && targetFilterLifeCycle) {
inner.destroy();
}
}
}
You can use it that way :
<bean id="samlFilter" class="...FilterWrapper" p:active="false">
<property name=inner>
<!-- the real samlFilter bean -->
<bean class="org.springframework.security.web.FilterChainProxy">
...
</bean>
</property>
</bean>
As it is a bean, you inject it where you want to activate/deactivate Saml and simple call :
samlFilter.setActive(active);

Spring Security 3.2.3 RELEASE with JavaConfig

I have a Spring Security configured in XML that works just fine. Now, I'm trying to have it expressed in JavaConfig only so as to get rid of the XML configuration altogether.
I've looked at the reference documentation, and at many blogs and support requests, but I still cannot find the solution.
It gives me the following exception:
Could not autowire field: private org.springframework.security.web.FilterChainProxy
com.thalasoft.learnintouch.rest.config.WebTestConfiguration.springSecurityFilterChain;
Pitifully I resorted to post my own request here...
The code:
#Configuration
#ComponentScan(basePackages = { "com.thalasoft.learnintouch.rest" })
public class WebTestConfiguration {
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private FilterChainProxy springSecurityFilterChain;
}
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
public class WebInit implements WebApplicationInitializer {
private static Logger logger = LoggerFactory.getLogger(WebInit.class);
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerListener(servletContext);
registerDispatcherServlet(servletContext);
registerJspServlet(servletContext);
}
private void registerListener(ServletContext servletContext) {
// Create the root application context
AnnotationConfigWebApplicationContext appContext = createContext(ApplicationConfiguration.class, WebSecurityConfiguration.class);
// Set the application display name
appContext.setDisplayName("LearnInTouch");
// Create the Spring Container shared by all servlets and filters
servletContext.addListener(new ContextLoaderListener(appContext));
}
private void registerDispatcherServlet(ServletContext servletContext) {
AnnotationConfigWebApplicationContext webApplicationContext = createContext(WebConfiguration.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(webApplicationContext));
dispatcher.setLoadOnStartup(1);
Set<String> mappingConflicts = dispatcher.addMapping("/");
if (!mappingConflicts.isEmpty()) {
for (String mappingConflict : mappingConflicts) {
logger.error("Mapping conflict: " + mappingConflict);
}
throw new IllegalStateException(
"The servlet cannot be mapped to '/'");
}
}
private void registerJspServlet(ServletContext servletContext) {
}
private AnnotationConfigWebApplicationContext createContext(final Class... modules) {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(modules);
return appContext;
}
}
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Bean
public DelegatingFilterProxy springSecurityFilterChain() {
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy();
return filterProxy;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").hasRole("ROLE_ADMIN").and().httpBasic();
http.authorizeRequests().antMatchers("/admin/login", "/admin/logout", "/admin/denied").permitAll()
.antMatchers("/admin/**").hasRole("ROLE_ADMIN")
.and()
.formLogin()
.loginPage("/admin/login")
.defaultSuccessUrl("/admin/list")
.failureUrl("/admin/denied?failed=true")
.and()
.rememberMe();
http.logout().logoutUrl("/admin/logout").logoutSuccessUrl("/admin/login").deleteCookies("JSESSIONID");
}
}
The XML configuration that I hope to get rid of:
<!-- A REST authentication -->
<http use-expressions="true" pattern="/admin/**">
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
<http-basic entry-point-ref="restAuthenticationEntryPoint" />
<logout />
</http>
<!-- A form based browser authentication -->
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/admin/login" access="permitAll" />
<intercept-url pattern="/admin/logout" access="permitAll" />
<intercept-url pattern="/admin/denied" access="permitAll" />
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
<form-login
login-page="/admin/login"
default-target-url="/admin/list"
authentication-failure-url="/admin/denied?failed=true"
always-use-default-target="true" />
<logout logout-success-url="/admin/login" />
<logout delete-cookies="JSESSIONID" />
</http>
<!-- A custom authentication provider on legacy data -->
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider" />
</authentication-manager>
UPDATE:
I added a Configuration directive:
#Configuration
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
and an explicit import directive:
#Import({ SecurityWebApplicationInitializer.class })
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
}
But the exception remained the exact same.
I'm running Spring Security 3.2.4.RELEASE and Spring 3.2.9.RELEASE
If you have any suggestion, it is welcomed.
I removed this bean definition from the security configuration and it seems to have solved the issue
#Bean
public DelegatingFilterProxy springSecurityFilterChain() {
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy();
return filterProxy;
}

How to use salt from database field in Spring Security

I am using spring-security-3.1.0 with spring-framework-3.0.6.
For login security check i'm using salt.But having problem in using salt in salt
source.
if i use beans:property name="userPropertyToUse" value="username" then
everything is fine
but having problem in <beans:property name="userPropertyToUse" value="lalt">
even tough i have configured all the necessary configuration for "salt".
It sowing this message
Unable to find salt method on user Object. Does the class 'org.springframework.security.core.userdetails.User'
have a method or getter named 'salt' ?
My spring-security.xml looks like this
<beans:bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource">
<beans:property name="userPropertyToUse" value="salt" />
</beans:bean>
<beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"/>
<beans:bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener" />
<authentication-manager>
<authentication-provider user-service-ref="jdbcUserService">
<password-encoder ref="passwordEncoder">
<salt-source ref="saltSource"/>
</password-encoder>
</authentication-provider>
</authentication-manager>
<beans:bean id="jdbcUserService" class="controllers.CustomJdbcDaoImpl">
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="usersByUsernameQuery">
<beans:value>SELECT U.userid AS username,
U.userpassword as password,
'true' AS enabled,
U.userpasswordsalt AS salt
FROM users U WHERE U.userid=?
</beans:value>
</beans:property>
<beans:property name="authoritiesByUsernameQuery">
<beans:value>SELECT U.userid AS username,
U.userrole as authority
FROM users U
WHERE U.userid=?
</beans:value>
</beans:property>
</beans:bean>
My jdbcUserService.java for salt is
public class CustomJdbcDaoImpl extends JdbcDaoImpl {
#Override
protected List<UserDetails> loadUsersByUsername(String username) {
return getJdbcTemplate().query(getUsersByUsernameQuery(),new String[] {username},
new RowMapper<UserDetails>() {
public UserDetails mapRow(ResultSet rs, int rowNum)throws SQLException {
String username = rs.getString(1);
String password = rs.getString(2);
boolean enabled = rs.getBoolean(3);
String salt = rs.getString(4);
System.out.println("CustomJdbcDaoImpl Salt : "+salt);
return new SaltedUser(username, password,enabled, true, true, true,AuthorityUtils.NO_AUTHORITIES, salt);
}
});
}
}
And My SaltedUser.java is
public class SaltedUser extends User{
private String salt;
public SaltedUser(String username, String password,boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked, List<GrantedAuthority>authorities, String salt) {
super(username, password, enabled,accountNonExpired, credentialsNonExpired,accountNonLocked, authorities);
this.salt = salt;
System.out.println("SaltedUser Salt : "+salt);
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
}
Any one can help me....?
You need to override the createUserDetails method which creates the final UserDetails implementation returned by the class. Take a look at the source for JdbcDaoImpl.
Note that if you aren't building this for a legacy system which already has a password-hashing system already in place, then using something like BCrypt to encode your passwords would be a better and simpler option.

Resources