Use Spring Security to secure Web applications
Security has always been a very important aspect of Web application development. Security, while a non-functional requirement of an application, should be considered early in application development. If security is considered at the late stage of application development, you may be caught in a dilemma: on the one hand, the application has serious security vulnerabilities, which cannot meet users’ requirements and may cause users’ private data to be stolen by attackers; On the other hand, the basic architecture of the application has been determined. In order to fix security vulnerabilities, it may be necessary to make major adjustments to the system architecture, which requires more development time and affects the application release process. Therefore, security considerations should be taken into account from the first day of application development and throughout the entire application development process.
This article detailed how to use Spring Security to secure Web applications. Spring Security itself, as well as the flexibility afforded by the Spring framework, meets the typical requirements of general Web application development and allows developers to customize. Let’s start with a brief introduction to Spring Security.
Spring Security profile
Spring is a very popular and successful Java application development framework. Spring Security provides a complete solution for Web application Security based on the Spring framework. Generally speaking, the security of Web applications includes Authentication and Authorization. User authentication refers to verifying whether a user is a legitimate subject in the system, that is, whether the user can access the system. User authentication generally requires a user name and password. The system verifies the user name and password to complete the authentication process. User authorization refers to verifying whether a user has permission to perform an operation. In a system, different users have different permissions. For example, some users can only read a file, while others can modify it. Generally speaking, the system assigns different roles to different users, and each role has a series of permissions.
The Spring Security framework has good support for both of the application scenarios mentioned above. In terms of user authentication, the Spring Security framework supports mainstream authentication methods, including HTTP basic authentication, HTTP form authentication, HTTP digest authentication, OpenID and LDAP. On the user authorization side, Spring Security provides role-based Access Control and Access Control Lists (ACLs) for fine-grained Control of domain objects in applications.
This article introduces the use of Spring Security through three concrete examples. The first example is a simple enterprise employee management system. There are three types of users in this system, which are ordinary employees, managers and presidents. Different types of users can access different resources. The operations that can be performed on these resources are also different. Spring Security helps developers meet these security-related requirements in a simple way. The second example shows how to integrate with an LDAP server. The third example shows how to integrate with OAuth. See Resources for the complete sample code. The following describes the implementation of basic user authentication and authorization.
Basic user authentication and authorization
This section introduces Spring Security from the basics of user authentication and authorization. Generally speaking, Web applications need to store user information on their own systems. This information is typically stored in a database. Users can register their own accounts, or be assigned by the system administrator. These users generally have their own roles, such as normal user and administrator. Some pages can only be accessed by users with specific roles, such as /admin. Here’s how to use Spring Security to meet such basic authentication and authorization requirements.
First, you need to introduce Spring Security to your Web application by adding a new filter to web.xml, as shown in Listing 1.
Listing 1. Add Spring Security’s filter to 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>Copy the code
Spring Security uses the standard filter mechanism in the Servlet specification. For a particular request, Spring Security’s filter checks whether the request is authenticated and whether the current user has sufficient permissions to access the resource. For illegal requests, the filter redirects to the specified page for authentication or returns an error message. Note that while only one filter is defined in Listing 1, Spring Security actually works with a chain of multiple filters.
The next step is to configure Spring Security to declare legitimate users in the system and their corresponding permissions. User information is through the org. Springframework. Security. Core. Populated userdetails. UserDetailsService interface to load. The only method for this interface is loadUserByUsername(String username), which loads relevant information based on the username. The return value of this method is org. Springframework. Security. Core. Populated userdetails. Populated userdetails interface, which contains the user’s information, including username, password, privilege, whether to enable, whether it is locked, if overdue, etc. One of the most important is the user permissions, by the org. Springframework. Security. Core. GrantedAuthority interface. Although the internal design and implementation of Spring Security can be complex, developers generally need to use only the implementation it provides by default to meet the requirements in most cases, with simple configuration declarations.
In the first example application, a database is used to store user information. Spring Security provides org. Springframework. Security. Core. Populated userdetails. JDBC. JdbcDaoImpl class to support the load from the database user information. Developers can use the class without any changes by simply using the database table structure that is compatible with the class. The configuration is shown in Listing 2.
Listing 2. Declares the use of a database to hold user information
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver" />
<property name="url" value="jdbc:derby://localhost:1527/mycompany" />
<property name="username" value="app" />
<property name="password" value="admin" />
</bean>
<bean id="userDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<sec:authentication-manager>
<sec:authentication-provider user-service-ref="userDetailsService" />
</sec:authentication-manager>Copy the code
As shown in Listing 2, you first define a data source that uses the Apache Derby database, Spring Security org. Springframework. Security. Core. Populated userdetails. JDBC. JdbcDaoImpl class USES the data source to load user information. Finally, you need to configure the authentication manager to use the UserDetailsService.
You can then configure user access to different resources. Resources here refer to URL addresses. The configuration is shown in Listing 3. SEC is the prefix of the namespace in which Spring Security’s configuration elements reside.
Listing 3. Configuring access to different URL patterns
<sec:http>
<sec:intercept-url pattern="/president_portal.do**" access="ROLE_PRESIDENT" />
<sec:intercept-url pattern="/manager_portal.do**" access="ROLE_MANAGER" />
<sec:intercept-url pattern="/**" access="ROLE_USER" />
<sec:form-login />
<sec:logout />
</sec:http>Copy the code
In the first example application, there are three roles defined: general user, manager, and President, represented by ROLE_USER, ROLE_MANAGER, and ROLE_PRESIDENT, respectively. The roles that users need to access the different URL patterns are defined in Listing 3. This is done with the < SEC: Intercept – URL > element, whose attribute pattern declares the pattern for requesting the URL, and the attribute access indicates the permissions required to access the URL. You need to declare the URL pattern in order from precise to fuzzy. Because Spring Security compares one URL at a time in the declared order, the request will be allowed as long as the user is currently accessing a URL that meets the declared permission requirements of a URL schema. If you put the URL schema /** declaration originally at the end of Listing 3 first, this request will also be allowed when an ordinary user accesses /manager_portal.do. This is clearly not true. The use of HTTP form authentication is declared with the < SEC :form-login> element. That is, when an unauthenticated user tries to access a restricted URL, the browser redirects to a login page that asks the user for a username and password. The < SEC :logout> element declares functionality that allows users to logout of the session. The default URL for logging out is /j_spring_security_logout, which can be modified with the logout-url property.
When you complete these configurations and run your application, you’ll find that Spring Security already provides an implementation of the login page by default that you can use directly. Developers can also customize the login page. Using the login-page, login-processing-URL, and authentication-failure-url attributes of
After showing you how to implement basic user authentication and authorization with Spring Security, let’s look at the core objects.
SecurityContext and Authentication objects
Let’s start by discussing some of the core objects in Spring Security. Org. Springframework. Security. Core. The context. SecurityContext interface representation is the security context of the current application. This interface is used to obtain and set the current authentication object. Org. Springframework. Security. Core. The Authentication interface used to indicate the Authentication object. You can use the authentication object to determine whether the current user is authenticated and obtain information about the current authenticated user, including the user name, password, and permission. To use this authentication object, you first need to get the SecurityContext object. By org. Springframework. Security. Core. Context. SecurityContextHolder class provides static methods getContext () can be obtained. The authentication object is then obtained via the getAuthentication() of the SecurityContext object. The current authentication principal is obtained through the getPrincipal() method of the authentication object, usually the implementation of the UserDetails interface. Referring to the UserDetailsService described in the previous section, a typical authentication process is that after a user enters a user name and password, the UserDetailsService finds the corresponding UserDetails object based on the user name and then compares the password for a match. If there is no match, an error message is returned; If a match is found, the user is successfully authenticated and an object that implements the Authentication interface is created. Such as org. Springframework. Security. Authentication. UsernamePasswordAuthenticationToken objects of a class. This authentication object is then set by the setAuthentication() method of the SecurityContext.
Listing 4 shows an example of using SecurityContext and Authentication to get the user name of the current authenticated user.
Listing 4. Getting the user name of the current authenticated user
public static String getAuthenticatedUsername() {
String username = null;
Object principal = SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
} else {
username = principal.toString();
}
return username;
}Copy the code
By default, the SecurityContextHolder uses ThreadLocal to hold the SecurityContext object. Therefore, the SecurityContext object is visible to all methods on the current thread. This implementation is suitable for Web applications. However, in some cases, such as desktop applications, this approach is not appropriate. Spring Security allows developers to customize this. Developers only need to implement the interface org. Springframework. Security. The core. The context. SecurityContextHolderStrategy and through SecurityContextHolder The setStrategyName(String) method lets Spring Security use this implementation. Another way to set this is to use system properties. In addition, Spring Security provides two other implementations by default: MODE_GLOBAL indicates that the current application shares a unique SecurityContextHolder; MODE_INHERITABLETHREADLOCAL indicates that the child thread inherits the parent thread’s SecurityContextHolder. Listing 5 shows an example of using a globally unique SecurityContextHolder.
Listing 5. Using a globally unique SecurityContextHolder
public void useGlobalSecurityContextHolder() { SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_GLOBAL); }Copy the code
Now that you’ve covered SecurityContext and Authentication in Spring Security, here’s how to secure the service layer.
Service layer method protection
The previous section described security protection at the URL granularity. This granularity of protection is insufficient in many cases. For example, on the page corresponding to the same URL, users of different roles may see different contents and perform different operations. In the first example application, each employee’s salary is recorded in the system. All employees can view their own salaries, but only the employee’s direct manager can modify an employee’s salary. This involves controlling permissions for the methods in the service layer of the application to avoid security vulnerabilities.
Protecting service-layer methods involves intercepting method calls in applications. Interception of method calls is easy with good aspect-oriented programming (AOP) support provided by the Spring framework. Spring Security leverages the power of AOP to allow declaratively defining the permissions needed to invoke a method. An example configuration file that protects method calls is shown in Listing 6.
Listing 6. Securing method calls
<bean id="userSalarySecurity"
class="org.springframework.security.access.intercept.aspectj.
AspectJMethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="accessDecisionManager" />
<property name="securityMetadataSource">
<value>
mycompany.service.UserService.raiseSalary=ROLE_MANAGER
</value>
</property>
</bean>Copy the code
As shown in Listing 6, Through mycompany. Service. UserService. RaiseSalary = ROLE_MANAGER declares the mycompany. Service. The UserService raiseSalary method only has the role of a class Only the ROLE_MANAGER user can execute this command. This prevents users with only the role ROLE_USER from calling this method.
There are other issues, however, that simply having permission control over method names doesn’t solve. For example, the salary increase in the first example application is done by sending an HTTP POST request to the salary.do URL. Salary. Do the corresponding controller mycompany. Controller. SalaryController invokes mycompany. Service. The UserService raiseSalary method to complete a wage increase of operation of a class. One security vulnerability is that users with the ROLE_MANAGER role can create HTTP POST requests using other tools, such as cURL or the Firefox extension Poster, to change other employees’ salaries. To solve this problem, more fine-grained control over the invocation of raiseSalary is needed. With AspectJ support provided by Spring Security, you can write the control logic, as shown in Listing 7.
Listing 7. Fine-grained control using AspectJ
public aspect SalaryManagementAspect { private AspectJMethodSecurityInterceptor securityInterceptor; private UserDao userDao; pointcut salaryChange(): target(UserService) && execution(public void raiseSalary(..) ) &&! within(SalaryManagementAspect); Object around(): salaryChange() { if (this.securityInterceptor == null) { return proceed(); } AspectJCallback callback = new AspectJCallback() { public Object proceedWithObject() { return proceed(); }}; Object[] args = thisJoinPoint.getArgs(); String employee = (String) args[0]; // The Username of the employee to be modified User User = userdao.getByUsername (employee); String currentUser = UsernameHolder.getAuthenticatedUsername(); // Current logged-in user if (! currentUser.equals(user.getManagerId())) { throw new AccessDeniedException ("Only the direct manager can change the salary."); } return this.securityInterceptor.invoke(thisJoinPoint, callback); }}Copy the code
As shown in Listing 7, a pointcut salaryChange and corresponding wrap enhancement are defined. When the method raiseSalary is invoked, the manager username of the employee to be modified is compared with the username of the currently logged in user. An AccessDeniedException is thrown when there is an inconsistency.
Now that I’ve seen how to protect method calls, I’ll show you how to protect domain objects through access control lists.
Access control list
All of the security and permission controls mentioned earlier apply only to URLS or method calls, and only to one class of objects. In some cases, different domain object entities require different permission controls. In the case of the first type of example application, there are reporting entities in the system. Due to the particularity of reports, only users with the role ROLE_PRESIDENT can create reports. For each report, the author can set its permissions to different users. For example, some reports can only be viewed by certain users. For such a requirement, access control rights need to be set for each instance of the domain object. Spring Security provides support for Access Control lists (ACLs) that make it easy to assign permissions to different domain objects for different users.
There are three important concepts in the implementation of access control lists in Spring Security, corresponding to four database tables.
- Subject of authorization: Generally, users in the system. by
ACL_SID
Table. - Domain objects: Represent entities in the system that require access control. by
ACL_CLASS
andACL_OBJECT_IDENTITY
The former holds the name of the Java class that corresponds to the entity, while the latter holds the entity itself. - Access rights: Represents the rights a user has on a domain object. By the table
ACL_ENTRY
To represent.
Spring Security already provides a reference database table schema and corresponding JDBC-based implementation. In most cases, a reference implementation will suffice. Class org. Springframework. Security. Acls. JDBC. JdbcMutableAclService to query the access control list, add, update, and delete operations, is most often developers directly using the class. The constructor of this class takes three parameters, Respectively is javax.mail. SQL. The DataSource said the data source, org. Springframework. Security. Acls. JDBC. LookupStrategy database query strategy and said Org. Springframework. Security. Acls. Model. AclCache said cache access control list. The data sources can use the data sources already in the first sample application. The realization of the query strategies you can use the default org. Springframework. Security. Acls. JDBC. BasicLookupStrategy. Cache can use the cache implementation based on EhCache org. Springframework. Security. Acls. Domain. EhCacheBasedAclCache. The code is shown in Listing 8.
Listing 8. Basic configuration of access control list services using JDBC
<bean id="aclService"
class="org.springframework.security.acls.jdbc.JdbcMutableAclService">
<constructor-arg ref="dataSource" />
<constructor-arg ref="lookupStrategy" />
<constructor-arg ref="aclCache" />
<property name="classIdentityQuery" value="values IDENTITY_VAL_LOCAL()"/>
<property name="sidIdentityQuery" value="values IDENTITY_VAL_LOCAL()"/>
</bean>Copy the code
As shown in listing 8, it is important to note org. Springframework. Security. Acls. JDBC. JdbcMutableAclService attribute classIdentityQuery and sidIdentityQuery. Spring Security’s default database schema uses auto-growing columns as the primary key. In the implementation, you need to be able to get the ID of the newly inserted column. Therefore, the SQL query language associated with the database implementation is required to obtain this ID. Spring Security uses HSQLDB by default, so the default values for these two properties are the CALL Identity () supported by HSQLDB. If you are using a database other than HSQLDB, you need to set the values of these two properties depending on the database implementation. The first example application uses the Apache Derby database, so the values of these two properties are values IDENTITY_VAL_LOCAL(). For MySQL, this value is select @@identity. Listing 9 shows the use org. Springframework. Security. Acls. JDBC. JdbcMutableAclService Java code to manage the access control list.
Listing 9. Using the Access control List service
public void createNewReport(String title, String content) throws ServiceException { final Report report = new Report(); report.setTitle(title); report.setContent(content); transactionTemplate.execute(new TransactionCallback<Object>() { public Object doInTransaction(TransactionStatus status) { reportDao.create(report); addPermission(report.getId(), new PrincipalSid(getUsername()), BasePermission.ADMINISTRATION); return null; }}); } public void grantRead(final String username, final Long reportId) { transactionTemplate.execute(new TransactionCallback<Object>() { public Object doInTransaction(TransactionStatus status) { addPermission(reportId, new PrincipalSid(username), BasePermission.READ); return null; }}); } private void addPermission(Long reportId, Sid recipient, Permission permission) { MutableAcl acl; ObjectIdentity oid = new ObjectIdentityImpl(Report.class, reportId); try { acl = (MutableAcl) mutableAclService.readAclById(oid); } catch (NotFoundException nfe) { acl = mutableAclService.createAcl(oid); } acl.insertAce(acl.getEntries().size(), permission, recipient, true); mutableAclService.updateAcl(acl); }Copy the code
The addPermission(Long reportId, Sid recipient, Permission) method in listing 9 is used to add access control to a report. The parameter reportId represents the reportId, Identifies a report; The Recipient represents the user who needs authorization; Permission indicates the permission granted. CreateNewReport () method is used to create a report, at the same time to create reports users with administrative privileges (BasePermission. The ADMINISTRATION). The grantRead() method is used to grant a user READ permission (basepermission-read) on a report. It is important to note that all operations on the ACCESS control list need to be handled in a single transaction. Use Spring to provide transaction template (org. Springframework. Transaction. Support. TransactionTemplate) can handle affairs. For permissions, Spring Security provides four basic permissions: read, write, delete, and manage. Developers can define their own permissions based on this.
With access control lists out of the way, let’s take a look at the JSP tag library provided by Spring Security.
JSP tag library
The previous sections showed you how to use the capabilities provided by Spring Security in Java code. In many cases, a user may have access to a page, but certain features on the page are not available to him. For example, for the same list of employees, a regular user can only view the data, while a user with a manager role can see links or buttons to modify the list, etc. Spring Security provides a JSP tag library to make it easy to control the display and hiding of certain parts of a JSP page based on the user’s permissions. Use the JSP tag library is very simple, you just need to add the statement on the JSP page: < % @ taglib prefix = “the SEC” uri = “http://www.springframework.org/security/tags” > %. The tag library contains the following three tags:
authorize
Tag: This tag is used to determine whether the content contained in it should be displayed. The criteria can be the evaluation result of an expression or access to a URL, respectively, through attributesaccess
andurl
To specify. Such as<sec:authorize access="hasRole('ROLE_MANAGER')">
Restricted content is visible only to users with the manager role.<sec:authorize url="/manager_portal.do">
Restrict content to urls only/manager_portal.do
Is visible to the user.authentication
Tag: This tag is used to get the current authentication object (Authentication
). Such as<sec:authentication property="principal.username" />
Can be used to obtain the user name of the current authentication user.accesscontrollist
Label: The function of the label andauthorize
Tags are similar in that they determine whether the content contained in them should be displayed. The difference is that it makes decisions based on access control lists. Attributes of the tagdomainObject
Represents domain objects, and propertieshasPermission
Represents the permissions to check. Such as<sec:accesscontrollist hasPermission="READ" domainObject="myReport">
Limits the content contained within the domain object onlymyReport
Only when you have read permission.
It is important to note that support for expressions needs to be enabled with < SEC: HTTP use-expressions=”true”> when using the Authorize tag. See the permission Control Expressions section to learn more about expressions.
Now that the JSP tag library is covered, let’s see how to integrate with LDAP.
Using LDAP
Many companies use LDAP servers to store employee information. All internal IT systems need to integrate with LDAP servers for user authentication and access authorization. Spring Security provides support for the LDAP protocol and requires simple configuration to enable Web applications to use LDAP for authentication. The second example application uses the OpenDS LDAP server and adds some test users. An example of a configuration file is shown in Listing 10; see Resources for the complete code.
Listing 10. Configuration file for integrating LDAP server
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"> <constructor-arg value="ldap://localhost:389" /> </bean> <bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider"> <constructor-arg> <bean class="org.springframework.security.ldap.authentication.BindAuthenticator"> <constructor-arg ref="contextSource" /> <property name="userSearch"> <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch"> <constructor-arg index="0" value="ou=People,dc=mycompany,dc=com" /> <constructor-arg index="1" value="(&(uid={0})(objectclass=person))" /> <constructor-arg index="2" ref="contextSource" /> </bean> </property> </bean> </constructor-arg> <constructor-arg> <bean class="mycompany.CompanyAuthoritiesPopulator"></bean> </constructor-arg> </bean> <sec:authentication-manager> <sec:authentication-provider ref="ldapAuthProvider" /> </sec:authentication-manager>Copy the code
Such as the code shown in listing 10, is the core part of the configuration class org. Springframework. Security.. Ldap authentication. LdapAuthenticationProvider, It is used for authentication with the LDAP server and obtaining user permission information. Generally speaking, there are two ways to authenticate with an LDAP server. One is to directly bind to the LDAP server using the user name and password provided by the user. Another way is to compare the password provided by the user to the password saved on the LDAP server. The former through class org. Springframework. Security.. Ldap authentication. BindAuthenticator, While the latter by class org. Springframework. Security.. Ldap authentication. PasswordComparisonAuthenticator. The second example application uses a binding approach for authentication. During binding, the current user needs to be searched on the LDAP server. During the search, you must specify a basic Distinguished Name and filter criteria. In this application, a user uses its unique identifier (UID) to log in, for example, user.0. On the LDAP server, the corresponding identifier is uid=user.0,ou=People,dc=mycompany,dc=com. Users can be searched and bound based on uid by using a filter condition (&(uid={0})(objectClass =person)). After the authentication succeeds, you need to obtain the permission of the user. This is usually determined by the group that the user belongs to on the LDAP server. However, the sample application shows how to provide your own implementation to assign permissions to users. Class mycompany.Com panyAuthoritiesPopulator implements org. Springframework. Security. Ldap. Populated userdetails. LdapAuthoritiesPopulator interface, A single role ROLE_USER is assigned to all users.
Now that you’ve covered integration with LDAP, here’s how to integrate with OAuth.
Request integration
Many Web services today provide APIS that allow third-party applications to use their data. When third-party applications need to access users’ private data, they need to be authenticated. OAuth is a popular authentication method used by many Web services, including Twitter, LinkedIn, Google Buzz and Sina Weibo. The feature of OAuth is that third-party applications can not directly obtain the user’s password, but only use a token after the user’s authorization to access. Users can manage third-party applications that can access their data and terminate third-party applications’ access to their data by retrieving tokens. The working mode of OAuth involves three main bodies: service providers, third-party applications and users. The basic workflow is as follows: third-party applications make requests to service providers to access user data. The service provider asks the user if they agree to this request. If the user agrees, the service provider returns a token to the third-party application. Third-party applications only need to carry this token when requesting data to successfully obtain it.
When third-party applications use OAuth authentication, the interaction involved is complicated. Spring Security itself does not provide OAuth support, which is available through another open source library, OAuth for Spring Security. OAuth for Spring Security is well integrated with Spring Security, making it easy to add OAuth support to existing applications that use Spring Security. However, OAuth for Spring Security currently only provides good support for Spring Security version 2.0.x. Support for OAuth consists of service providers and service consumers: a service provider is a provider of data, and a service consumer is a third-party application that uses the data. The typical application is to serve consumers. OAuth for Spring Security provides support for both service providers and consumers. Here’s an example of getting status updates from LinkedIn to illustrate its use.
As a service consumer of OAuth, you need to request a key from the service provider to represent its application. The service provider provides three urls to interact with the service consumer. The configuration file for using OAuth for Spring Security is shown in Listing 11.
Listing 11. Using the OAuth for Spring Security configuration file
<oauth:consumer resource-details-service-ref="linkedInResourceDetails" oauth-failure-page="/oauth_error.jsp"> <oauth:url pattern="/linkedin.do**" resources="linkedIn" /> </oauth:consumer> <bean id="oauthConsumerSupport" class="org.springframework.security.oauth.consumer.CoreOAuthConsumerSupport"> <property name="protectedResourceDetailsService" ref="linkedInResourceDetails" /> </bean> <oauth:resource-details-service id="linkedInResourceDetails"> <oauth:resource id="linkedIn" key="***" secret="***" request-token-url="https://api.linkedin.com/uas/oauth/requestToken" user-authorization-url="https://www.linkedin.com/uas/oauth/authorize" access-token-url="https://api.linkedin.com/uas/oauth/accessToken" /> </oauth:resource-details-service>Copy the code
As shown in Listing 11, a simple configuration of the
element is required to declare the use of LinkedIn’s services. Each
element corresponds to an OAuth service resource. The attributes of this element contain information related to the service resource. OAuth for Spring Security adds an additional filter implementation to handle OAuth authentication in addition to the filters provided by Spring Security. The
child of
defines the URL pattern to which the filter works and the corresponding OAuth service resource. When a user accesses a specified URL, the application goes to the service provider’s page and asks the user for authorization. When a user is authorized, the application can access their data. When accessing data, additional Authorization headers need to be added to HTTP requests. Listing 12 shows the code to use when accessing the data.
Listing 12. Getting the access token and building the HTTP request
public OAuthConsumerToken getAccessTokenFromRequest(HttpServletRequest request) { OAuthConsumerToken token = null; List<OAuthConsumerToken> tokens = (List<OAuthConsumerToken>) request .getAttribute(OAuthConsumerProcessingFilter.ACCESS_TOKENS_DEFAULT_ATTRIBUTE); if (tokens ! = null) { for (OAuthConsumerToken consumerToken : tokens) { if (consumerToken.getResourceId().equals(resourceId)) { token = consumerToken; break; } } } return token; } public GetMethod getGetMethod(OAuthConsumerToken accessToken, URL url) { GetMethod method = new GetMethod(url.toString()); method.setRequestHeader("Authorization", getHeader(accessToken, url, "GET")); return method; } public String getHeader(OAuthConsumerToken accessToken, URL url, String method) { ProtectedResourceDetails details = support .getProtectedResourceDetailsService() .loadProtectedResourceDetailsById(accessToken.getResourceId()); return support.getAuthorizationHeader(details, accessToken, url, method, null); }Copy the code
As shown in Listing 12, OAuth for Spring Security’s filter saves the token after OAuth has authenticated successfully in the current request. Through getAccessTokenFromRequest () method can get the token from the request. Once you have this token, you can use the getHeader() method to build the Authorization headers required for HTTP requests. All you need to do is add this HTTP header to the request and you can access the required data normally. By default, the application’s OAuth token is stored in the HTTP session, and the developer can provide other ways to save the token, such as in the database. Only need to provide org. Springframework. Security. Request. Consumer. The token. OAuthConsumerTokenServices interface implementation.
Now that you’ve covered the way you integrate with OAuth, here are some advanced topics.
Advanced topics
These advanced topics related to Spring Security include permission control expressions, session management, and remembering users.
Permission control expression
In some cases, the conditions for accessing a certain resource may be complex. It is not just a simple requirement that the current user has a certain role, but a combination of multiple conditions. Permission control expressions allow a simple syntax to describe more complex authorization conditions. Spring Security has some common expressions built in, including hasRole() to determine whether the current user has a role, hasAnyRole() to determine whether the current user has a role in a list, HasPermission () is used to determine whether the current user has certain permissions on domain objects. These basic expressions can be combined with and and OR to represent complex semantics. When expression support is enabled with < SEC: HTTP use-true “>, expressions can be used on the access attribute of the < SEC: Intercept – URL > element.
Expressions can also be used for permission control over method calls, primarily in method annotations. To enable method annotations provided by Spring Security, I need to add the < global-method-Security pre-post-annotations=”enabled”/> element. The method annotations are:
@PreAuthorize
This annotation is used to determine whether a method should be executed. This annotation is followed by an expression, and if the value of the expression is true, the method is executed. Such as@PreAuthorize("hasRole('ROLE_USER')")
Indicates that only the current user has a roleROLE_USER
“Will be executed.@PostAuthorize
This annotation is used to perform an access control check after the method completes execution.@PostFilter
This annotation is used to filter the results returned by the method. Filter elements whose expression value is false from the returned collection. Such as@PostFilter("hasPermission(filterObject, 'read')")
Note Only the elements that the current user has read permission are reserved in the returned result.@PreFilter
This annotation is used to filter the parameters of a method call.
Session management
Spring Security provides the ability to manage HTTP sessions. These include Session timeout management, Session fixation attack defense, and concurrent Session management.
If the current user’s session expires due to timeout, Spring Security can detect this and jump to the appropriate page if the user continues to access the session. < SEC: HTTP > < SEC :session-management invalid-session-url=”/sessionTimeout.jsp” /> The invalid-session-url attribute specifies the URL to be forwarded to after the session times out.
Some Web applications directly pass the session identifier of the user through the PARAMETERS of the URL, and do not perform authentication on the server. For example, the URL accessed by the user may be/myURL. Jsessionid = XXX. An attacker can construct a URL with a known session identifier and send this URL to the target. If the attacker accesses this URL and successfully logs in with his own user name, the attacker can use this authenticated session to access the attacked data. One way to protect against such attacks is to require users to re-authenticate before doing anything important. Spring Security protects against this attack by allowing developers to customize the handling of existing sessions when a user logs in. You can modify this behavior with the session-fixation-protection attribute of the < SEC :session-management> element. The optional values for this property are migrateSession, newSession, and None. MigrateSession is the default value. In this case, a new session is created each time the user logs in, and the data from the previous session is copied into the new session. NewSession indicates that only new sessions are created, and data is not copied. None means to keep the previous session.
In some cases, the application may need to limit the number of sessions that can result from simultaneous logins using the same user name. For example, some applications may require a maximum of one session per user at a time. You can limit the number of concurrent sessions per user by using the < SEC :session-management> child of the < SEC :concurrency- Control > element. For example, < SEC :concurrency- Control max-sessions=”2″ /> limits each user to a maximum of two sessions at a time. If the current user’s number of sessions has reached the upper limit and the user logs in again, the default implementation invalidates the previous session. If you want to prevent the subsequent login, set the value of the error-if-maximum-exceeded attribute to true. In this case, the subsequent login will fail. The user can log in again only after the previous session expires.
Remember that the user
Some Web applications provide a check box on the login screen that asks the user if they want to remember their password on the current computer. If this option is selected, the user does not need to enter the user name and password to log in to the application within a period of time. Spring Security provides support for remembering this user’s needs. Just add the < SEC :remember-me> element to < SEC: HTTP >.
In general, there are two ways to achieve the ability to remember a user. One approach is to make use of browser-side cookies. When the user successfully logs in, a string of specific content is saved in a cookie. The contents saved in the cookie are used to authenticate the user the next time the user visits. This is used by default. The use of cookies has security risks. For example, attackers may steal users’ cookies and use them to log in to the system. A more secure alternative is for browser-side cookies to store only random numbers that can only be used once and are regenerated each time a user logs in. These numbers are stored in a server-side database. If you want to use this approach, you create a database table and specify the data source containing the table through the data-source-ref attribute.
conclusion
For Web applications developed using Spring, Spring Security is the best choice for increased Security. This article covered various aspects of Spring Security in detail, including implementing basic user authentication and authorization, securing the service layer approach, securing specific domain-objects with access control lists, JSP tag libraries, and integration with LDAP and OAuth. In this article, developers can learn how to use Spring Security to implement different user authentication and authorization mechanisms.
Download resources
- Simple enterprise staff management system (Company.zip | 48 KB) : does not contain dependent jars. Use createdB.sql to create the database.
- With LDAP integration (LDAPSample.zip | 9 KB) : does not contain dependent jars. Modify the configuration file based on the LDAP server address and structure.
- With the integration of the request and use LinkedIn service (LinkedInSample. Zip | 17 KB) : does not contain dependent jars. Use your LinkedIn developer account to modify your profile.
On the topic
- Check out the reference documentation for Spring Security version 3.0.x.
- Understand the common authentication modes: HTTP basic authentication, HTTP digest authentication, and HTTP form authentication.
- Learn how to safely implement remember user functionality.
- Learn more about session setting attacks.
- Check out more about OAuth.
- Check out LinkedIn’s development documentation.
- Download the Spring Security 3.0.3 release.
- Download the SpringSource toolkit suite to facilitate the development of Spring-based applications.
- Download the Apache Derby database.
- Download EhCache as an implementation of caching.
- Download the OpenDS LDAP server.
- Download OAuth for Spring Security to integrate with OAuth.
- DeveloperWorks Java Technology zone: Hundreds of articles on all aspects of Java programming.