Spring Security openid-login attribute-exchange only call once at first time - spring

I have trouble in spring security 3.1.
I'm going to use spring security openid-login with gmail, and I want to get user information using attribute-exchange. but if I use it, it is always called when user log-in my website.
How can i call only once at user sign-in my website?
I managed sign-in at openIdAuthFailureHandler, and I want to get user information in this bean...please help me!
(I found security:remember-me, but it doesn't work..)
security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
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.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<security:global-method-security
secured-annotations="enabled" proxy-target-class="true" />
<security:http auto-config="true" access-denied-page="/denied/accessDenied">
<security:intercept-url pattern="/admin/**"
access="ROLE_ADMIN" />
<security:intercept-url pattern="/reservation/**"
access="ROLE_USER, ROLE_ADMIN" />
<security:intercept-url pattern="/board/**"
access="ROLE_ADMIN, ROLE_USER" />
<security:openid-login login-page="/"
login-processing-url="/j_spring_openid_security_check.do"
authentication-success-handler-ref="customAuthenticationHandler"
authentication-failure-handler-ref="openIdAuthFailureHandler">
<security:attribute-exchange identifier-match="https://www.google.com/.*" >
<security:openid-attribute name="email" type="http://schema.openid.net/contact/email" required="true" />
<security:openid-attribute name="firstname" type="http://axschema.org/namePerson/first" required="true" />
</security:attribute-exchange>
</security:openid-login>
<security:logout logout-url="/j_spring_openid_security_logout.do"
logout-success-url="/" invalidate-session="true" />
<!-- <security:http-basic /> -->
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<!-- <security:user-service properties="/WEB-INF/resources/users.xml"
/> -->
<security:password-encoder ref="passwordEncoder" />
<security:jdbc-user-service id="userDetailsService"
data-source-ref="dataSource"
users-by-username-query="SELECT id as id, passwd as passwd, 1 as enabled FROM user WHERE id=?"
authorities-by-username-query="SELECT id as id, power as authority FROM user WHERE id=?" />
<!-- <security:password-encoder hash="sha-256"></security:password-encoder> -->
</security:authentication-provider>
</security:authentication-manager>
<bean id="customTokenRepository" class="com.jinyoung.reservation.openid.CustomTokenRepository" />
<bean id="openIdAuthFailureHandler" class="com.jinyoung.reservation.openid.OpenIDAuthenticationFailureHandler"/>
<bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" />
</beans>
OpenIDAuthenticationFailureHandler
public class OpenIDAuthenticationFailureHandler extends
SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
if (exception instanceof UsernameNotFoundException && exception.getAuthentication() instanceof OpenIDAuthenticationToken && ((OpenIDAuthenticationToken) exception.getAuthentication()).getStatus().equals(OpenIDAuthenticationStatus.SUCCESS)) {
DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
request.getSession(true).setAttribute("USER_OPENID_CREDENTIAL", ((UsernameNotFoundException)exception).getExtraInformation());
OpenIDAuthenticationToken openIdAuth = (OpenIDAuthenticationToken)exception.getAuthentication();
request.getSession(true).setAttribute("USER_OPENID_CREDENTIAL_EXTRA", openIdAuth);
for(OpenIDAttribute attr : openIdAuth.getAttributes()) {
System.out.printf("AX Attribute: %s, Type: %s, Count: %d\n", attr.getName(), attr.getType(), attr.getCount());
for(String value : attr.getValues()) {
System.out.printf(" Value: %s\n", value);
}
}
redirectStrategy.sendRedirect(request, response, "/login/registrationOpenid");
// redirect to create account page
/*redirectStrategy.sendRedirect(request, response,
"/?fail=true");*/
} else {
super.onAuthenticationFailure(request, response, exception);
}
}
}

Ran in to a similar problem which I solved by having two different form targets (j_spring_openid_security_check_signup and j_spring_openid_security_check) for the signup/login page. One used when signing up and one when logging in. In spring-security.xml we use two different configs, one that asks for attributes and one that don't:
<!-- Configure attribute-exchange for signup -->
<!-- will only match /j_spring_openid_security_check_signup -->
<security:http auto-config="true" use-expressions="true" path-type="ant" pattern="/j_spring_openid_security_check_signup">
<security:openid-login
login-processing-url="/j_spring_openid_security_check_signup"
user-service-ref="userDetailsService"
authentication-failure-handler-ref="authenticationFailureHandler"
authentication-success-handler-ref="authenticationSuccessHandler">
<security:attribute-exchange identifier-match="https://www.google.com/.*" >
<security:openid-attribute name="email" type="http://schema.openid.net/contact/email" required="true" />
<security:openid-attribute name="firstname" type="http://axschema.org/namePerson/first" required="true" />
</security:attribute-exchange>
</security:openid-login>
</security:http>
<!-- Skip attribute-exchange at log-in -->
<!-- match everything else -->
<security:http auto-config="true" use-expressions="true" path-type="ant">
<security:openid-login
login-processing-url="/j_spring_openid_security_check"
user-service-ref="userDetailsService"
authentication-failure-handler-ref="authenticationFailureHandler"
authentication-success-handler-ref="authenticationSuccessHandler"
/>
</security:http>
Then in the authenticationSuccessHandler you will have access to attributes while signing in but not at log-in.

-------------------------------I solved!!!------------------------------------------
I modified spring-security-openid-3.1.1.RELEASE.jar, and I call attribute-change only once at user access my site first time. if one who want to know, give me e-mail kjy30532#gmail.com !

Related

UserDetailsService is not invoked in spring security

I am trying to use spring security with two login page and configure two different http section in my spring-security.xml I also have two authentication-manager which handles authentication using custom UserDetailsService.
Below are my code snippets
spring-security.xml
<!-- http Section for Admins -->
<http auto-config="true" use-expressions="true" authentication-manager-ref="adminAuth" pattern="/admin/**">
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
<!-- access denied page -->
<access-denied-handler error-page="/403" />
<form-login login-page="/adminLogin" default-target-url="/admin/home" authentication-failure-url="/views/homePages/adminHome" username-parameter="username"
password-parameter="password"
login-processing-url="/authenticateAdmin" />
<logout logout-success-url="/login?logout" />
<!-- enable csrf protection -->
<csrf disabled="true"/>
</http>
<authentication-manager id="adminAuth" alias="adminAuth">
<authentication-provider user-service-ref='adminAuthService'>
<password-encoder hash="md5" />
</authentication-provider>
</authentication-manager>
<beans:bean id="adminAuthService" class="security.AdminAuth">
</beans:bean>
<!-- http Section for Students -->
<http auto-config="true" use-expressions="true" authentication-manager-ref="studentAuth" pattern="/student/**" >
<intercept-url pattern="/**" access="hasRole('ROLE_STUDENT')" />
<!-- access denied page -->
<access-denied-handler error-page="/403" />
<form-login login-page="/studentLogin" default-target-url="/student/home" authentication-failure-url="/studentLogin" login-processing-url="/authenticateStudent" />
<logout logout-success-url="/studentLogin" />
<!-- enable csrf protection -->
<csrf disabled="true"/>
</http>
<authentication-manager id="studentAuth" alias="studentAuth">
<authentication-provider user-service-ref='studentAuthService'>
<password-encoder hash="md5" />
</authentication-provider>
</authentication-manager>
<beans:bean id="studentAuthService" class="security.StudentAuth">
</beans:bean>
UserDetailService implementor class
package security;
#Service
public class AdminAuth implements UserDetailsService {
#Autowired
AdminService adminService;
public UserDetails loadUserByUsername(String authAttribute) throws UsernameNotFoundException {
Admin admin = getAdminByUname(authAttribute);
List<SimpleGrantedAuthority> authList = new ArrayList();
authList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
User user = new User(authAttribute, admin.getPassword(), authList);
return user;
}
private Admin getAdminByUname(String authAttribute) {
Admin admin = adminService.getAdminbyUname(authAttribute);
return admin;
}
}
Admin login page
<h2 class="text-center">Staff Login</h2>
<form class="login-form" action="/authenticateAdmin" method="POST">
<div class="form-group">
<label for="exampleInputEmail1" class="text-uppercase">Username</label> <input type="text" class="form-control" placeholder="Email or Mobile" id="username">
</div>
<div class="form-group">
<label for="exampleInputPassword1" class="text-uppercase">Password</label> <input type="password" class="form-control" placeholder="" id="password">
</div>
<div class="form-check">
<label class="form-check-label"> <input type="checkbox" class="form-check-input"> <small>Remember Me</small>
</label>
<button type="submit" class="btn btn-login float-right">Submit</button>
</div>
</form>
Now when I open login page enter username/password and hit login then my loadUserByUsername is not getting invoked.
What I doing wrong here.

configuring spring-security and spring-mvc

I'm trying to configure my spring-security project for first time but when I try to login I receive a message: "ERR_TOO_MANY_REDIRECTS". May be somewhere under the hood it redirects to admin again, but where?
here is my spring-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<http use-expressions="true" >
<csrf disabled="true"/>
<intercept-url pattern="/admin" access="hasRole('Admin')" />
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/logout" access="permitAll" />
<access-denied-handler error-page="/403" />
<form-login login-page='/login' login-processing-url="/login" authentication-failure-url="/login?error=true"
username-parameter="username" password-parameter="password" />
<logout logout-url="/logout" logout-success-url="/logoutSuccessful" delete-cookies="JSESSIONID" invalidate-session="true" />
</http>
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="myDataSource"
users-by-username-query= "select login, password, 'true' from employee where login=?"
authorities-by-username-query= "select login, role from employee where login =? " />
</authentication-provider>
</authentication-manager>
<beans:import resource="data-source-cfg.xml"/>
</beans:beans>
controller class:
#Controller
#RequestMapping("/")
public class HelloController {
#RequestMapping(method = RequestMethod.GET)
public String printWelcome(ModelMap model) {
return "login";
}
#RequestMapping(value="/admin", method = RequestMethod.GET)
public String showAdmin(ModelMap model) {
return "admin";
}
#RequestMapping(value="/login", method=RequestMethod.GET)
public String enter(#ModelAttribute("employee") Employee employee, ModelMap model){
return "redirect:/admin";
}
...
Table with user's login and role looks like:
Employee
|id|login |password |first_name|last_name|mobile_phone|role |
|1 |login1 |password1 |name1 |lname1 |phone1 |User |
|2 |login2 |password2 |name2 |lname2 |phone2 |Admin |
spring security version is 4.0.3.
This is my first experience with spring-mvc and spring-security so the question may be noobie. What is wrong?
The problem is you are simply redirecting to the admin page on the /login resource, which is where it will send people if they are not logged in. Since /admin requires a specific role, it will try to go to the login page if the user is not logged in which you have redirecting back to the admin page, thus creating a loop.
You want to return a view and not redirect back to the admin page.

Spring security logout - add a message only when logout triggered from a logged in user

Lets say my logout URL is: app/logout.
I need to show a message - "You are succesfully logged out" in logout page only when logout was triggred by clikcing the logout button.
The message should not be displayed if the user enters this URL directly. Any idea how to implement this?
Controller:
#RequestMapping(value ="/logout", method = RequestMethod.GET)
public ModelAndView logout(HttpServletRequest request, HttpServletResponse response) {
ModelAndView model = new ModelAndView();
//some code here to distingish
model.addObject("msg", "You are succesfully logged out!");
model.setViewName("login");
return model;
}
Spring-Security.xml:
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-4.0.xsd">
<http auto-config="true" create-session="ifRequired" use-expressions="true">
<intercept-url pattern="/logout" access="IS_AUTHENTICATED_REMEMBERED"/>
<form-login
login-page="/login"
default-target-url="/home"
authentication-failure-url="/login?error"
username-parameter="username"
password-parameter="password" />
<logout logout-success-url="/logout" invalidate-session="true" delete-cookies="JSESSIONID"/>
<!-- enable csrf protection -->
<csrf/>
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="mkyong" password="123456" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
As it is for Spring Security, I think that the simplest way is to use spring security to restrict /logout to authenticated users.
Using the namespace it would be :
<intercept-url pattern="/logout" access="IS_AUTHENTICATED_REMEMBERED"/>
Using Java configuration, it would be :
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/logout").authenticated()
...
}
Add #RequestParam(value = "foo", required = false) Boolean foo
Add this additional parameter to logout button, if foo exist and is true do your logic

Spring security 3.1.4 backdoor

I have this problem:
in a java web-app (with spring and spring-security 3.1.4) there's a sso authentication; this means the user authenticates as soon as he log in on his pc.
The configuration is this:
<sec:http>
<sec:logout />
<sec:form-login login-page="/login.jsp" default-target-url="/" />
<sec:anonymous username="guest" granted-authority="ROLE_GUEST" />
<sec:custom-filter ref="headersFilter" after="SECURITY_CONTEXT_FILTER" />
<sec:custom-filter ref="jaasFilter" after="SERVLET_API_SUPPORT_FILTER" />
</sec:http>
and this works (actually login.jsp doesn't exist because the user is already logged in as I said above).
Now the problem is that I want to have a "backdoor";this means there should be a login page for me and my team to test and mantain the app.
It should work like this:
-I call localhost/wepapp/myloginpage and I should see the myloginpage.jsp (this works now);
-I click on "login" button and I enter in the second " element" and if the login is ok then I should get redirected to "/" (this doesn't work and I'm simply redirected on "login");
-with the configuration below it seems that I can see "/" without authentication, too, if I call it (localhost/wepapp)
I tried this configuration but it doesn't work, I mean I can see "/" without authentication and I get redirected to login (I also tried other small variations but same result, more or less):
<sec:http pattern="/myloginpage">
<sec:logout />
<sec:form-login login-page="/myloginpage" default-target-url="/" />
</sec:http>
<sec:http pattern="/login">
<sec:logout />
<sec:form-login login-page="/login" default-target-url="/" />
<sec:anonymous username="guest" granted-authority="ROLE_GUEST" />
<sec:custom-filter ref="headersFilter" after="SECURITY_CONTEXT_FILTER" />
<sec:custom-filter ref="jaasFilter" after="SERVLET_API_SUPPORT_FILTER" />
</sec:http>
My myloginpage.jsp:
<form action="login" method="POST">
<table>
<tr>
<td>
Name
</td>
<td>
<input type="text" name="name">
</td>
</tr>
.........
</form>
I also have the controller for myloginpage:
#Controller
public class Myloginpage {
publicMyloginpage() {
}
#RequestMapping("/myloginpage")
public String home() {
return "myloginpage";
}
}
Thankx,
Adrian
It seems you are missing the <intercept-url> tags to configure access to certain paths.
<sec:intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<sec:intercept-url pattern="/secure/**" access="ROLE_USER" />

Spring security authenticating error

I have an application which I have configured so I can use database to authenticate users.
Trying to login as either user or admin with the set up username and password gives this error:
HTTP Status 404 - /project/login_error.jsp
So I tried to see if my url were working manually by putting the links in the navigation bar, the admin page comes up when I use this link:
https://localhost:8443/project/admin
Some other page also come up when entered manually, others do not show but gives me the error page.
The home page on the other hand does not show at all when I use this:
https://localhost:8443/project/home
It does not give any error but just redirects back to the login page.
No error comes up in the stack trace except for the hibernate HQL.
The security config:
<global-method-security pre-post-annotations="enabled" />
<http pattern="/resources/css/**" security="none"/>
<http pattern="/login.jsp*" security="none"/>
<http auto-config="true" use-expressions="true" access-denied-page="/denied"
authentication-manager-ref="authManager">
<intercept-url pattern="/**" requires-channel="https" />
<intercept-url pattern="/index" access="permitAll" requires-
channel='https'/>
<intercept-url pattern="/login" access="permitAll" requires-
channel='https'/>
<intercept-url pattern='/home' access="hasRole('ROLE_USER')" requires-
channel='https'/>
<intercept-url pattern='/admin' access="hasRole('ROLE_ADMIN')" requires-
channel='https'/>
<form-login login-page='/login' login-processing-url='/j_spring_security_check'
always-use-default-target="true" default-target-url="/home" authentication-failure-
url="/login_error.jsp?error=true"/>
<logout invalidate-session="true" logout-success-url='/login' />
</http>
<authentication-manager id="authManager">
<authentication-provider user-service-ref="***UserDetailsService">
<password-encoder hash="md5"/>
</authentication-provider>
</authentication-manager>
<beans:bean id="**UserDetailsService" class="com.**.**.**.**UserDetailsService">
</beans:bean>
Controller
#Controller
public class ApplicationController {
#RequestMapping(value="/home", method = RequestMethod.GET)
public String home(ModelMap model) {
logger.info("Download and Upload Page {}.");
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
return "home";
}
#RequestMapping(value="/login", method = RequestMethod.GET)
public String login(ModelMap model) {
logger.info("This is the login page {}.");
return "login";
}
Jsp page
<c:url value="/j_spring_security_check" var="loginUrl"/>
<form action="${loginUrl}" method="post" >
<label for="j_username">Username</label>
<input id="j_username" name="j_username" type="text" />
<label for="j_password">Password</label>
<input id="j_password" name="j_password" type="password" />
<input type="submit"
value="Login"/>
</form>
The HQL that shows in the output
Hibernate: select login0_.person_id as person1_1_, login0_1_.manager_id as
manager4_1_, login0_1_.email as email1_, login0_1_.name as name1_, login0_.enabled
as enabled3_, login0_.password as password3_, login0_.user_id as user5_3_,
login0_.username as username3_ from users login0_ inner join person login0_1_ on
login0_.person_id=login0_1_.person_id where login0_.username=?
I don't really know what I'm missing in this implementation. And I would appreciate any pointers on what the problem is.
Edit - adding security filter
<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>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>

Resources