缓存UserDetails
CachingUserDetailsService类的构造接收一个用于真正加载UserDetails的UserDetailsService实现类。
当需要加载UserDetails时,其首先会从缓存中获取,如果缓存中没有对应的UserDetails存在,则使用持有的UserDetailsService实现类进行加载,然后将加载后的结果存放在缓存中。
UserDetails与缓存的交互是通过UserCache接口来实现的。
当我们需要对UserDetails进行缓存时,我们只需要定义一个Ehcache实例,然后把它注入给EhCacheBasedUserCache就可以了
<security:authentication-manager alias="authenticationManager">
<!-- 使用可以缓存UserDetails的CachingUserDetailsService -->
<security:authentication-provider
user-service-ref="cachingUserDetailsService" />
</security:authentication-manager>
<!-- 可以缓存UserDetails的UserDetailsService -->
<bean id="cachingUserDetailsService" class="org.springframework.security.config.authentication.CachingUserDetailsService">
<!-- 真正加载UserDetails的UserDetailsService -->
<constructor-arg ref="userDetailsService"/>
<!-- 缓存UserDetails的UserCache -->
<property name="userCache">
<bean class="org.springframework.security.core.userdetails.cache.EhCacheBasedUserCache">
<!-- 用于真正缓存的Ehcache对象 -->
<property name="cache" ref="ehcache4UserDetails"/>
</bean>
</property>
</bean>
<!-- 将使用默认的CacheManager创建一个名为ehcache4UserDetails的Ehcache对象 -->
<bean id="ehcache4UserDetails" class="org.springframework.cache.ehcache.EhCacheFactoryBean"/>
<!-- 从数据库加载UserDetails的UserDetailsService -->
<bean id="userDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
退出登录
<security:logout logout-url="/logout.do"/>
logout其他属性
属性名 | 作用 |
---|---|
invalidate-session | 表示是否要在退出登录后让当前session失效,默认为true。 |
delete-cookies | 指定退出登录后需要删除的cookie名称,多个cookie之间以逗号分隔。 |
logout-success-url | 指定成功退出登录后要重定向的URL。需要注意的是对应的URL应当是不需要登录就可以访问的。 |
success-handler-ref | 指定用来处理成功退出登录的LogoutSuccessHandler的引用。 |
基于方法的权限控制
intercept-methods定义方法权限控制
<!--以find开始的方法时需要当前用户拥有ROLE_USER的权限
在执行方法名以add、update或delete开始的方法时需要拥有ROLE_ADMIN的权限-->
<bean id="userService" class="com.xxx.service.impl.UserServiceImpl">
<security:intercept-methods>
<security:protect access="ROLE_USER" method="find*"/>
<security:protect access="ROLE_ADMIN" method="add*"/>
<security:protect access="ROLE_ADMIN" method="update*"/>
<security:protect access="ROLE_ADMIN" method="delete*"/>
</security:intercept-methods>
</bean>
使用pointcut定义方法权限控制
<!--在执行com.elim包或其子包下任意以Service结尾的类,其方法名以find开始的所有方法时都需要用户拥有ROLE_READ的权限
对于com.elim包或其子包下任意以Service结尾的类的其它方法在执行时都需要ROLE_WRITE的权限-->
<security:global-method-security>
<security:protect-pointcut access="ROLE_READ" expression="execution(* com.elim.*..*Service.find*(..))"/>
<security:protect-pointcut access="ROLE_WRITE" expression="execution(* com.elim.*..*Service.*(..))"/>
</security:global-method-security>
使用注解定义方法权限控制
JSR-250注解、@Secured注解和支持表达式的注解
JSR-250注解
<security:global-method-security jsr250-annotations="enabled"/>
@Service
@RolesAllowed("ROLE_ADMIN")//所有的方法都需要角色ROLE_ADMIN
public class UserServiceImpl implements UserService {
public void addUser(User user) {
System.out.println("addUser................" + user);
}
public void updateUser(User user) {
System.out.println("updateUser.............." + user);
}
@RolesAllowed({"ROLE_USER", "ROLE_ADMIN"})
public User find(int id) {
System.out.println("find user by id............." + id);
return null;
}
public void delete(int id) {
System.out.println("delete user by id................");
}
@RolesAllowed("ROLE_USER")
public List<User> findAll() {
System.out.println("find all user...............");
return null;
}
}
- @RolesAllowed:表示访问对应方法时所应该具有的角色。其可以标注在类上,也可以标注在方法上
- @PermitAll:表示允许所有的角色进行访问。其可以标注在类上,也可以标注在方法上
- @DenyAll:表示无论什么角色都不能访问。只能定义在方法上。脱离了权限控制还是可以访问的。
方法上的优先度高于类上的
@Secured注解
<security:global-method-security secured-annotations="enabled"/>
@Service
public class UserServiceImpl implements UserService {
@Secured("ROLE_ADMIN")
public void addUser(User user) {
System.out.println("addUser................" + user);
}
@Secured("ROLE_USER")
public List<User> findAll() {
System.out.println("find all user...............");
return null;
}
}
支持表达式的注解
四个支持使用表达式的注解,分别是@PreAuthorize、@PostAuthorize、@PreFilter和@PostFilter。其中前两者可以用来在方法调用前或者调用后进行权限检查,后两者可以用来对集合类型的参数或者返回值进行过滤。
<!--启动 enabled ,默认disabled-->
<security:global-method-security pre-post-annotations="disabled"/>
使用@PreAuthorize和@PostAuthorize进行访问控制
@Service
public class UserServiceImpl implements UserService {
/**
*只有拥有角色ROLE_ADMIN的用户才能访问
*/
@PreAuthorize("hasRole('ROLE_ADMIN')")
public void addUser(User user) {
System.out.println("addUser................" + user);
}
/**
*有ROLE_USER角色或ROLE_ADMIN角色能访问
*/
@PreAuthorize("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
public User find(int id) {
System.out.println("find user by id............." + id);
return null;
}
/**
* 限制只能查询Id小于10的用户
*/
@PreAuthorize("#id<10")
public User find(int id) {
System.out.println("find user by id........." + id);
return null;
}
/**
* 限制只能查询自己的信息
*/
@PreAuthorize("principal.username.equals(#username)")
public User find(String username) {
System.out.println("find user by username......" + username);
return null;
}
/**
* 限制只能新增用户名称为abc的用户
*/
@PreAuthorize("#user.name.equals('abc')")
public void add(User user) {
System.out.println("addUser............" + user);
}
/**
*在方法find()调用完成后进行权限检查,返回值的id是偶数则表示校验通过,否则表示校验失败
*/
@PostAuthorize("returnObject.id%2==0")
public User find(int id) {
User user = new User();
user.setId(id);
return user;
}
}
使用@PreFilter和@PostFilter对集合类型的参数或返回值进行过滤
/**
*对返回结果中id不为偶数的user进行移除
*lterObject是使用@PreFilter和@PostFilter时的一个内置表达式,表示集合中的当前对象
*/
@PostFilter("filterObject.id%2==0")
public List<User> findAll() {
List<User> userList = new ArrayList<User>();
User user;
for (int i=0; i<10; i++) {
user = new User();
user.setId(i);
userList.add(user);
}
return userList;
}
当@PreFilter标注的方法拥有多个集合类型的参数时
需要通过@PreFilter的filterTarget属性指定当前@PreFilter是针对哪个参数进行过滤的
/**
*通过filterTarget指定了当前@PreFilter是用来过滤参数ids
*/
@PreFilter(filterTarget="ids", value="filterObject%2==0")
public void delete(List<Integer> ids, List<String> usernames) {
...
}