I am using a Spring Security, Restful WebService and Postman Rest Clien. i want to test my Restful WebService using the Postman without logging in and I do put a #Secured("AMIN_ROLE") to my methods for Restful webservice in order to secured the method, and using the POSTMAN Authorization tab i also put a Digest Auth which is the username and password with already MD5 format. now my problem is I cannot test my restful webservice using the Postman it will prompt a UNAUTHORIZED. please help me for this stuff...
security.xml
<security:http
use-expressions="true"
entry-point-ref="authEntryPoint">
<security:intercept-url pattern="/" access="permitAll" />
<security:intercept-url pattern="/index.jsp" access="permitAll" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>
</security:http>
<security:authentication-manager alias="authManager">
<security:authentication-provider ref="authProvider"/>
</security:authentication-manager>
<bean id="authProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="passwordEncoder" ref="md5Encoder"/>
</bean>
<bean id="md5Encoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"/>
<bean id="userDetailsService" class="com.shindi.ippan.service.UserAccountService"/>
UserAccountService.java
#Service("userAccountService")
public class UserAccountService extends AbstractService implements UserDetailsService{
#Autowired
#Qualifier("userAccountRepository")
IRepository<UserAccount> repository;
#Override
protected IRepository<UserAccount> getRepository() {
return repository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserAccount userAccount = ((UserAccountRepository)repository).readByUsername(username);
return userAccount;
}
}
UserAccount.java implements UserDetails
ApplicationAuthenticationEntryPoint.java
#Component("authEntryPoint")
public class ApplicationAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "MENSAHE UNAUTHORIZED");
}
}
web.xml
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
UserAccountController.java
#Controller
#RequestMapping("/userAccount")
public class UserAccountController extends AbstractController {
#Autowired
#Qualifier("userAccountService")
IService<UserAccount> service;
#Override
public IService<UserAccount> getService() {
return service;
}
#RequestMapping(value="/login", method=RequestMethod.POST)
public #ResponseBody UserAccount login( String username, String password ){
UserAccount userAccount = ((UserAccountService)service).loadUserByUsername(username);
return userAccount;
}
#Secured("ADMIN_ROLE")
#RequestMapping(value="/test", method=RequestMethod.POST)
public #ResponseBody String test(){
return "test...";
}
#RequestMapping(value="/denied", method=RequestMethod.POST)
public #ResponseBody String denied(){
return "denied...";
}
}
Related
I'm working with Spring Security 4 XML based configuration.
This is my configuration (security.xml):
<!--?xml version="1.0" encoding="UTF-8"?-->
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<http create-session="always"
use-expressions="true"
authentication-manager-ref="authenticationManager"
entry-point-ref="authenticationEntryPoint">
<csrf disabled="true" />
<intercept-url pattern="/**" access="hasRole('USER')" />
<form-login authentication-success-handler-ref="customAuthenticationSuccessHandler" />
<logout />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDao"></authentication-provider>
</authentication-manager>
</beans:beans>
Here is my CustomEntryPoint:
#Component
public class CustomEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
System.out.println("Entering commence due to failed Authentication");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized Access!");
}
}
and my UserDao (for future reading credentials from file):
public class UserDao implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String password = readPasswordFromFileOrDatabase(username);
if (password == null) throw new UsernameNotFoundException("failure");
return User.withUsername("user").password(password).authorities("ROLE_USER").build();
}
private String readPasswordFromFileOrDatabase(String username) {
if (username.equals("user")) return "q";
return null;
}
}
And it looks like REST feauture doesn't work, when I send POST through Postman to http://localhost:8080/login with user/password: user/q it says
Bad credentials
But when I do the same thing through form in browser it works fine.
So, is there any way to make REST features work?
The following code will give you base64 encoded value of username and password. Use that to add a HTTP header as following:
String plainClientCredentials="myusername:mypassword";
String base64ClientCredentials = new String(Base64.encodeBase64(plainClientCredentials.getBytes()));
System.out.println(base64ClientCredentials);`
In headers set this:
Key- Authorization, Value-Basicbase64ClientCredentials
Suppose the code prints 123&78#
then value would be Basic123&78#.
I am using java config for spring security and I am trying to replace this code with no luck
<security:custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
cant find any info about how to use the position in java config
Update i am trying to replace this code by java config but with no luck
<security:http
realm="Protected API"
use-expressions="true"
auto-config="false"
create-session="stateless"
entry-point-ref="unauthorizedEntryPoint"
authentication-manager-ref="authenticationManager">
<security:custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
<security:intercept-url pattern="/rest/user/authenticate" access="permitAll" />
<security:intercept-url method="GET" pattern="/rest/news/**" access="hasRole('user')" />
<security:intercept-url method="PUT" pattern="/rest/news/**" access="hasRole('admin')" />
<security:intercept-url method="POST" pattern="/rest/news/**" access="hasRole('admin')" />
<security:intercept-url method="DELETE" pattern="/rest/news/**" access="hasRole('admin')" />
</security:http>
<bean id="unauthorizedEntryPoint" class="net.dontdrinkandroot.example.angularrestspringsecurity.rest.UnauthorizedEntryPoint" />
<bean class="net.dontdrinkandroot.example.angularrestspringsecurity.rest.AuthenticationTokenProcessingFilter" id="authenticationTokenProcessingFilter">
<constructor-arg ref="userDao" />
</bean>
and this is my AuthenticationTokenProcessingFilter
public class AuthenticationTokenProcessingFilter extends UsernamePasswordAuthenticationFilter
{
private final UserDetailsService userService;
public AuthenticationTokenProcessingFilter(UserDetailsService userService)
{
this.userService = userService;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException
{
HttpServletRequest httpRequest = this.getAsHttpRequest(request);
String authToken = this.extractAuthTokenFromRequest(httpRequest);
String userName = TokenUtils.getUserNameFromToken(authToken);
if (userName != null) {
UserDetails userDetails = this.userService.loadUserByUsername(userName);
if (TokenUtils.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
private HttpServletRequest getAsHttpRequest(ServletRequest request)
{
if (!(request instanceof HttpServletRequest)) {
throw new RuntimeException("Expecting an HTTP request");
}
return (HttpServletRequest) request;
}
private String extractAuthTokenFromRequest(HttpServletRequest httpRequest)
{
/* Get token from header */
String authToken = httpRequest.getHeader("X-Auth-Token");
/* If token not found get it from request parameter */
if (authToken == null) {
authToken = httpRequest.getParameter("token");
}
return authToken;
}
Hope this is clearer
Here are the filter classes in the order of execution and with the addFilter method of the HttpSecurity class you add your own filters:
#Override
public void configure(HttpSecurity http) throws Exception {
http.addFilter(new AuthenticationTokenProcessingFilter());
...
You have to either extend or provide an instance of the defined Spring filters. The order is based on the class or superclass so you don't have to add the position:
JavaDoc
Here is my spring security config:
<http pattern="/auth/login" security="none" />
<http pattern="/auth/loginFailed" security="none" />
<http pattern="/resources/**" security="none" />
<http auto-config="true" access-decision-manager-ref="accessDecisionManager">
<intercept-url pattern="/auth/logout" access="permitAll"/>
<intercept-url pattern="/admin/**" access="ADMINISTRATIVE_ACCESS"/>
<intercept-url pattern="/**" access="XYZ_ACCESS"/>
<form-login
login-page="/auth/login"
authentication-failure-url="/auth/loginFailed"
authentication-success-handler-ref="authenticationSuccessHandler" />
<logout logout-url="/auth/logout" logout-success-url="/auth/login" />
</http>
The authenticationSuccessHandler extends the SavedRequestAwareAuthenticationSuccessHandler ensuring that the user is redirected to the page he originally requested.
However, since /auth/login is marked as security="none", I am unable to successfully redirect the user to the homepage if he accesses the login page after being logged in. I believe this is the right user experience too.
I tried the below too but the Principal object is always null, presumably because of the security="none" attribute again.
#RequestMapping(value = "/auth/login", method = GET)
public String showLoginForm(HttpServletRequest request, Principal principal) {
if(principal != null) {
return "redirect:/";
}
return "login";
}
I've checked the topic more deeply than last time and found that you have to determine if user is authenticated by yourself in controller. Row Winch (Spring Security dev) says here:
Spring Security is not aware of the internals of your application
(i.e. if you want to make your login page flex based upon if the user
is logged in or not). To show your home page when the login page is
requested and the user is logged in use the SecurityContextHolder in
the login page (or its controller) and redirect or forward the user to
the home page.
So solution would be determining if user requesting /auth/login is anonymous or not, something like below.
applicationContext-security.xml:
<http auto-config="true" use-expressions="true"
access-decision-manager-ref="accessDecisionManager">
<intercept-url pattern="/auth/login" access="permitAll" />
<intercept-url pattern="/auth/logout" access="permitAll" />
<intercept-url pattern="/admin/**" access="ADMINISTRATIVE_ACCESS" />
<intercept-url pattern="/**" access="XYZ_ACCESS" />
<form-login login-page="/auth/login"
authentication-failure-url="/auth/loginFailed"
authentication-success-handler-ref="authenticationSuccessHandler" />
<logout logout-url="/auth/logout" logout-success-url="/auth/login" />
</http>
<beans:bean id="defaultTargetUrl" class="java.lang.String">
<beans:constructor-arg value="/content" />
</beans:bean>
<beans:bean id="authenticationTrustResolver"
class="org.springframework.security.authentication.AuthenticationTrustResolverImpl" />
<beans:bean id="authenticationSuccessHandler"
class="com.example.spring.security.MyAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" ref="defaultTargetUrl" />
</beans:bean>
Add to applicationContext.xml bean definition:
<bean id="securityContextAccessor"
class="com.example.spring.security.SecurityContextAccessorImpl" />
which is class
public final class SecurityContextAccessorImpl
implements SecurityContextAccessor {
#Autowired
private AuthenticationTrustResolver authenticationTrustResolver;
#Override
public boolean isCurrentAuthenticationAnonymous() {
final Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
return authenticationTrustResolver.isAnonymous(authentication);
}
}
implementing simple interface
public interface SecurityContextAccessor {
boolean isCurrentAuthenticationAnonymous();
}
(SecurityContextHolder accessing code is decoupled from controller, I followed suggestion from this answer, hence SecurityContextAccessor interface.)
And last but not least redirect logic in controller:
#Controller
#RequestMapping("/auth")
public class AuthController {
#Autowired
SecurityContextAccessor securityContextAccessor;
#Autowired
#Qualifier("defaultTargetUrl")
private String defaultTargetUrl;
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String login() {
if (securityContextAccessor.isCurrentAuthenticationAnonymous()) {
return "login";
} else {
return "redirect:" + defaultTargetUrl;
}
}
}
Defining defaultTargetUrl String bean seems like a hack, but I don't have better way not to hardcode url... (Actually in our project we use <util:constant> with class containing static final String fields.) But it works after all.
You could also restrict your login page to ROLE_ANONYMOUS and set an <access-denied-handler />:
<access-denied-handler ref="accessDeniedHandler" />
<intercept-url pattern="/auth/login" access="ROLE_ANONYMOUS" />
And in your handler check if the user is already authenticated:
#Service
public class AccessDeniedHandler extends AccessDeniedHandlerImpl {
private final String HOME_PAGE = "/index.html";
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && !(auth instanceof AnonymousAuthenticationToken)) {
response.sendRedirect(HOME_PAGE);
}
super.handle(request, response, e);
}
}
Implement a Redirect Interceptor for this purpose:
The Interceptor (implementing HandlerInterceptor interface) check if someone try to access the login page, and if this person is already logged in, then the interceptor sends a redirect to the index page.
public class LoginPageRedirectInterceptor extends HandlerInterceptorAdapter {
private String[] loginPagePrefixes = new String[] { "/login" };
private String redirectUrl = "/index.html";
private UrlPathHelper urlPathHelper = new UrlPathHelper();
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (isInLoginPaths(this.urlPathHelper.getLookupPathForRequest(request))
&& isAuthenticated()) {
response.setContentType("text/plain");
sendRedirect(request, response);
return false;
} else {
return true;
}
}
private boolean isAuthenticated() {
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return false;
}
if (authentication instanceof AnonymousAuthenticationToken) {
return false;
}
return authentication.isAuthenticated();
}
private void sendRedirect(HttpServletRequest request,
HttpServletResponse response) {
String encodedRedirectURL = response.encodeRedirectURL(
request.getContextPath() + this.redirectUrl);
response.setStatus(HttpStatus.SC_TEMPORARY_REDIRECT);
response.setHeader("Location", encodedRedirectURL);
}
private boolean isInLoginPaths(final String requestUrl) {
for (String login : this.loginPagePrefixes) {
if (requestUrl.startsWith(login)) {
return true;
}
}
return false;
}
}
You can keep it simple flow by access-denied-page attribute in http element or as dtrunk said to write handler for access denied as well as. the config would be like
<http access-denied-page="/403" ... >
<intercept-url pattern="/login" access="ROLE_ANONYMOUS" />
<intercept-url pattern="/user/**" access="ROLE_USER" />
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<form-login login-page="/login" default-target-url="/home" ... />
...
</http>
in controller for /403
#RequestMapping(value = "/403", method = RequestMethod.GET)
public String accessDenied() { //simple impl
return "redirect:/home";
}
and for /home
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String home(Authentication authentication) {
// map as many home urls with Role
Map<String, String> dashBoardUrls = new HashMap<String, String>();
dashBoardUrls.put("ROLE_USER", "/user/dashboard");
dashBoardUrls.put("ROLE_ADMIN", "/admin/dashboard");
String url = null;
Collection<? extends GrantedAuthority> grants = authentication
.getAuthorities();
// for one role per user
for (GrantedAuthority grantedAuthority : grants) {
url = dashBoardUrls.get(grantedAuthority.getAuthority());
}
if (url == null)
return "/errors/default_access_denied.jsp";
return "redirect:" + url;
}
and when you make request for /admin/dashboard without logged in, it will redirect /login automatically by security
<http pattern="/login" auto-config="true" disable-url-rewriting="true">
<intercept-url pattern="/login" access="ROLE_ANONYMOUS"/>
<access-denied-handler error-page="/index.jsp"/>
</http>
You can try checking
if(SecurityContextHolder.getContext().getAuthentication() == null)
True means the user isn't authenticated, and thus can be sent to the login page. I don't know how robust/reliable this is, but it seems reasonable to try.
I followed more or less this description to create a custom login form:
http://technology-for-human.blogspot.com/2011/01/jsf-2-with-spring-3-protection-with.html
The login form uses a LoginBean, and the LoginBean uses an AuthenticationService:
#Service("authenticationService")
public class AuthenticationService {
#Resource(name = "authenticationManager")
AuthenticationManager authenticationManager;
public boolean login(String username, String password) {
try {
Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
if (authenticate.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authenticate);
return true;
}
} catch (AuthenticationException e) {
}
return false;
}
}
Generally everything works as expected, the login methods return true for my user. But then the login-form appears again and I can see that SecurityContextHolder.getContext().getAuthentication() returns null.
The Spring config looks like this:
<security:http auto-config='true'>
<security:form-login login-page="/login.jsf" />
<security:intercept-url pattern="/login.jsf" filters="none" />
<security:intercept-url pattern="/**" access="ROLE_USER" />
<security:logout logout-url="/logout" logout-success-url="/" />
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="userDetailsService" />
</security:authentication-manager>
Any ideas what probably could be the reason?
I think the security context is not persisted back to the session. Do you have the springSecurityFilterChain configured as a filter in the web.xml?
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
How can I add my own logout handler to LogoutFilter in spring-security ?
Thanks!
The following solution works for me and may be helpful:
Extend the SimpleUrlLogoutSuccessHandler or implement the LogoutHandler:
public class LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
// Just for setting the default target URL
public LogoutSuccessHandler(String defaultTargetURL) {
this.setDefaultTargetUrl(defaultTargetURL);
}
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// do whatever you want
super.onLogoutSuccess(request, response, authentication);
}
}
Add to your Spring Security Configuration:
<security:logout logout-url="/logout" success-handler-ref="logoutSuccessHandler" />
<bean id="logoutSuccessHandler" class="your.package.name.LogoutSuccessHandler" >
<constructor-arg value="/putInYourDefaultTargetURLhere" />
</bean>
See the answer in this post in the Spring Security Forum:
XML Definition:
<beans:bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter">
<custom-filter position="LOGOUT_FILTER"/>
<beans:constructor-arg index="0" value="/logout.jsp"/>
<beans:constructor-arg index="1">
<beans:list>
<beans:ref bean="securityContextLogoutHandler"/>
<beans:ref bean="myLogoutHandler"/>
</beans:list>
</beans:constructor-arg>
</beans:bean>
<beans:bean id="securityContextLogoutHandler" class="org.springframework.security.ui.logout.SecurityContextLogoutHandler"/>
<beans:bean id="myLogoutHandler" class="com.whatever.CustomLogoutHandler">
<beans:property name="userCache" ref="userCache"/>
</beans:bean>
LogoutHandler class:
public class CustomLogoutHandler implements LogoutHandler {
private UserCache userCache;
public void logout(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) {
// ....
}
#Required
public void setUserCache(final UserCache userCache) {
this.userCache = userCache;
}
}
You can use java-config solutions like this.
#Configuration
#EnableWebSecurity
public class SpringSecurity2Config extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//you can set other security config by call http.XXX()
http
.logout()
.addLogoutHandler(new CustomLogoutHandler())
.logoutUrl("/logout")
.logoutSuccessHandler(...)
.permitAll();
}
static class CustomLogoutHandler implements LogoutHandler {
#Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
//...
}
}
}
You should use success-handler-ref attribute of <logout> element:
<security:logout invalidate-session="true"
success-handler-ref="myLogoutHandler"
logout-url="/logout" />
As alternative solution you can configure your own filter on the logout URL.