问题
I was looking for a concrete, serious and complete example on how to use Spring security in a Spring Boot application that uses Spring data repositories to access the database and therefore to query about registered users.
I've seen that it's easily protected a range of webpages using Spring security by overriding the configure
method, for example with the following options:
http.authorizeRequests()
.antMatchers("/", "/css/**", "/js/**", "/vendor/**", "/templates/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
This code protects users for example from accessing http://localhost:3000/home/users/
, but allows then to access http://localhost:3000/login
or simply http://localhost:3000
.
I've been reading around about Spring security, but I can't get how can I protect the different parts of an application, for example, when a user has logged in to the website, and prohibit him from accessing from example http://localhost:3000/home/users/another_user
, and in general to control the access of a logged user to all parts of the website.
I'm using Spring data repositories to manipulate data of the database through the entities.
Do you know about an example that uses Spring security in conjunction with Spring repositories (and if necessary other tools) to protect (and authenticate) different parts of a website? A (video-)tutorial may also be useful.
Thanks for any help.
Note: I've looked at the sagan website's repository, but it's quite complex to understand what's going on...
回答1:
This is called Access Control, or "Domain Object Security" in Spring Security.
http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#domain-acls
You've got a lot of reading to do!
You probably want to combine it with Spring Data JPA, so that SDJ only returns the records it should. An example here:
https://github.com/spring-projects/spring-data-examples/tree/master/jpa/security
Basically you're going to be adding some "row owner" info to your tables, and getting SDJ and SS to work together to control access, like:
@Query("select o from BusinessObject o where o.owner.emailAddress like ?#{hasRole('ROLE_ADMIN') ? '%' : principal.emailAddress}")
List<BusinessObject> findBusinessObjectsForCurrentUser();
An alternative it to use a database server that supports Row Security, like PostgreSQL, and handle your access control right in the db.
回答2:
As noted above ACLs are one option however an alternative and possibly simpler solution may to be to apply security at the method level.
See section 15.3.
https://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html
So supposing you have an URL /users/123 where 123 is the current user and which delegates to a service layer method to load the user then how do I prevent the user from tampering with the URL and seeing data returned by e.g. /users/456.
One approach is to apply method level security via the @PostAuthorize annotation:
@PostAuthorize("hasPermission(returnObject, null)")
public User findById(Long id) {
return repository.findOne(id);
}
The security checks are delegated to an implementation of org.springframework.security.access.PermissionEvaluator
An implementation could look like the below:
public class BasePermissionsEvaluator implements PermissionEvaluator {
public boolean hasPermission(Authentication authentication, Object domainObject) {
return hasPermission(authentication, domainObject, null);
}
@Override
public boolean hasPermission(Authentication authentication, Object domainObject, Object permission) {
boolean hasPermission = true;
//User is my custom class representing a logged in user
//UserEntity is my custom interface implemented by entities associated with specific user
//If user is an Admin allow access
//Otherwise allow access if logged in user 'owns' the DomainObject instance
User user = (User) authentication.getPrincipal();
if(! user.isAdmin()){
if (domainObject instanceof UserEntity) {
User owner = ((UserEntity) domainObject).getOwner();
hasPermission = user.equals(owner);
}
}
return hasPermission;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,
Object permission) {
return false;
}
}
Configuration of the PermissionEvaluator looks like the following in XML so you would need to convert to Java config:
<security:global-method-security
pre-post-annotations="enabled">
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>
<bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>
<bean id="permissionEvaluator" class="com.mycompany.BasePermissionsEvaluator" />
The following outlines converting the XML config to Java config:
https://spring.io/blog/2013/07/04/spring-security-java-config-preview-method-security/#custom-method-security
So in your existing security configuration class it looks like you would add:
@EnableGlobalMethodSecurity(prePostEnabled=true) //ADD THIS
public class MySecurityConfig{
@Override
protected MethodSecurityExpressionHandler expressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
//SET TO OUR CUSTOM PERMISSIONS HANDLER DETAILED ABOVE
expressionHandler.setPermissionEvaluator(new BasePermissionsEvaluator());
return expressionHandler;
}
}
来源:https://stackoverflow.com/questions/37057175/spring-security-in-conjuction-with-spring-repositories-to-secure-and-authenticat