登录流程
1)容器启动(MySecurityMetadataSource:loadResourceDefine加载系统资源与权限列表)
2)用户发出请求
3)过滤器拦截(MySecurityFilter:doFilter)
4)取得请求资源所需权限(MySecurityMetadataSource:getAttributes)
5)匹配用户拥有权限和请求权限(MyAccessDecisionManager:decide),如果用户没有相应的权限,
执行第6步,否则执行第7步。
6)登录
7)验证并授权(MyUserDetailServiceImpl:loadUserByUsername)
1、web.xml中加入过滤器
- <!-- SpringSecurity 核心过滤器配置 -->
- <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>
- <?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.xsd
- http://www.springframework.org/schema/security
- http://www.springframework.org/schema/security/spring-security.xsd">
- <!-- entry-point-ref 配置自定义登录 -->
- <http use-expressions="true" entry-point-ref="authenticationProcessingFilterEntryPoint">
- <!-- 登出配置 -->
- <logout logout-url="/j_spring_security_logout" logout-success-url="/login" />
- <access-denied-handler error-page="/noPower" />
- <!-- 过滤不被拦截的请求 -->
- <intercept-url pattern="/login*" access="permitAll" />
- <intercept-url pattern="/resources/**" access="permitAll" />
- <!-- 只有权限才能访问的请求 -->
- <intercept-url pattern="/admin/**" access="isAuthenticated()" />
- <custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER" />
- <custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR" />
- </http>
- <beans:bean id="loginFilter"
- class="cn.com.abel.test.service.security.MyUsernamePasswordAuthenticationFilter">
- <!-- 登录提交处理 -->
- <beans:property name="filterProcessesUrl" value="/j_spring_security_check"></beans:property>
- <!-- 登录成功跳转 -->
- <beans:property name="authenticationSuccessHandler"
- ref="loginLogAuthenticationSuccessHandler"></beans:property>
- <!-- 设置登录失败的网址 -->
- <beans:property name="authenticationFailureHandler"
- ref="simpleUrlAuthenticationFailureHandler"></beans:property>
- <!-- 用户拥有权限 -->
- <beans:property name="authenticationManager" ref="myAuthenticationManager"></beans:property>
- </beans:bean>
- <beans:bean id="loginLogAuthenticationSuccessHandler"
- class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
- <beans:property name="defaultTargetUrl" value="/admin/index"></beans:property>
- </beans:bean>
- <beans:bean id="simpleUrlAuthenticationFailureHandler"
- class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
- <beans:property name="defaultFailureUrl" value="/login"></beans:property>
- </beans:bean>
- <authentication-manager alias="myAuthenticationManager">
- <authentication-provider user-service-ref="myUserDetailServiceImpl">
- <password-encoder ref="encoder" />
- </authentication-provider>
- </authentication-manager>
- <beans:bean id="myUserDetailServiceImpl"
- class="cn.com.abel.test.service.security.AdminUserDetailServiceImpl">
- </beans:bean>
- <beans:bean id="authenticationProcessingFilterEntryPoint"
- class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
- <beans:property name="loginFormUrl" value="/login"></beans:property>
- </beans:bean>
- <!-- 认证过滤器 -->
- <beans:bean id="securityFilter"
- class="cn.com.abel.test.service.security.MySecurityFilter">
- <!-- 用户拥有的角色 -->
- <beans:property name="authenticationManager" ref="myAuthenticationManager" />
- <!-- 用户是否拥有所请求资源的权限 -->
- <beans:property name="accessDecisionManager" ref="myAccessDecisionManager" />
- <!-- 资源与角色的对应关系 -->
- <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" />
- <!-- <beans:property name="rejectPublicInvocations" value="true"/> -->
- </beans:bean>
- <beans:bean id="myAccessDecisionManager" class="myAccessDecisionManager"></beans:bean>
- <beans:bean id="mySecurityMetadataSource"
- class="cn.com.abel.test.service.security.MySecurityMetadataSource">
- <beans:constructor-arg>
- <beans:ref bean="resourceService" />
- </beans:constructor-arg>
- </beans:bean>
- <beans:bean id="encoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"></beans:bean>
- </beans:beans>
3、MyUsernamePasswordAuthenticationFilter.java
- package cn.com.abel.test.service.security;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.security.authentication.AuthenticationServiceException;
- import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
- import org.springframework.security.core.Authentication;
- import org.springframework.security.core.AuthenticationException;
- import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
- public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
- @Override
- public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
- if (!request.getMethod().equals("POST")) {
- throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
- }
- String username = obtainUsername(request);
- String password = obtainPassword(request);
- if (username == null) {
- username = "";
- }
- if (password == null) {
- password = "";
- }
- username = username.trim();
- UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
- // Allow subclasses to set the "details" property
- setDetails(request, authRequest);
- // //登录验证码,如需要开启把下面注释去掉则可
- // String authCode = StringUtils.defaultString(request.getParameter("authCode"));
- // if(!AdwImageCaptchaServlet.validateResponse(request, authCode)){
- // throw new AuthenticationServiceException("validCode.auth.fail");
- // }
- return this.getAuthenticationManager().authenticate(authRequest);
- }
- }
4、MySecurityFilter.java
- package cn.com.abel.test.service.security;
- import java.io.IOException;
- import javax.servlet.Filter;
- import javax.servlet.FilterChain;
- import javax.servlet.FilterConfig;
- import javax.servlet.ServletException;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import org.springframework.security.access.SecurityMetadataSource;
- import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
- import org.springframework.security.access.intercept.InterceptorStatusToken;
- import org.springframework.security.web.FilterInvocation;
- import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
- public class MySecurityFilter extends AbstractSecurityInterceptor implements Filter {
- //与applicationContext-security.xml里的myFilter的属性securityMetadataSource对应,
- //其他的两个组件,已经在AbstractSecurityInterceptor定义
- private FilterInvocationSecurityMetadataSource securityMetadataSource;
- @Override
- public SecurityMetadataSource obtainSecurityMetadataSource() {
- return this.securityMetadataSource;
- }
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
- FilterInvocation fi = new FilterInvocation(request, response, chain);
- invoke(fi);
- }
- private void invoke(FilterInvocation fi) throws IOException, ServletException {
- // object为FilterInvocation对象
- //1.获取请求资源的权限
- //执行Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object);
- //2.是否拥有权限
- //获取安全主体,可以强制转换为UserDetails的实例
- //1) UserDetails
- // Authentication authenticated = authenticateIfRequired();
- //this.accessDecisionManager.decide(authenticated, object, attributes);
- //用户拥有的权限
- //2) GrantedAuthority
- //Collection<GrantedAuthority> authenticated.getAuthorities()
- //System.out.println("用户发送请求! ");
- InterceptorStatusToken token = null;
- token = super.beforeInvocation(fi);
- try {
- fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
- } finally {
- super.afterInvocation(token, null);
- }
- }
- public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
- return securityMetadataSource;
- }
- public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {
- this.securityMetadataSource = securityMetadataSource;
- }
- public void init(FilterConfig arg0) throws ServletException {
- // TODO Auto-generated method stub
- }
- public void destroy() {
- // TODO Auto-generated method stub
- }
- @Override
- public Class<? extends Object> getSecureObjectClass() {
- //下面的MyAccessDecisionManager的supports方面必须放回true,否则会提醒类型错误
- return FilterInvocation.class;
- }
- }
5、AdminUserDetailServiceImpl.java
- package cn.com.abel.test.service.security;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Set;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.security.core.GrantedAuthority;
- import org.springframework.security.core.authority.SimpleGrantedAuthority;
- import org.springframework.security.core.userdetails.UserDetails;
- import org.springframework.security.core.userdetails.UserDetailsService;
- import org.springframework.security.core.userdetails.UsernameNotFoundException;
- import cn.com.abel.test.model.RoleModel;
- import cn.com.abel.test.model.MemberModel;
- import cn.com.abel.test.service.RoleService;
- import cn.com.abel.test.service.MemberService;
- public class AdminUserDetailServiceImpl implements UserDetailsService {
- @Autowired
- private MemberService memberService;
- @Autowired
- RoleService roleService;
- //登录验证
- public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
- MemberModel member = memberService.getUserDetailsByUserName(username);
- if(member==null){
- throw new UsernameNotFoundException("member "+username +" not found.");
- }
- Set<GrantedAuthority> grantedAuths = obtionGrantedAuthorities(member);
- //封装成spring security的user
- User userdetail = new User(user.getUserName(), user.getPassword(),
- true, // 账号状态 0 表示停用 1表示启用
- true, true, true, grantedAuths // 用户的权限
- );
- return userdetail;
- }
- //取得用户的权限
- private Set<GrantedAuthority> obtionGrantedAuthorities(MemberModel member) {
- Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
- List<RoleModel> roles = roleService.getRoleByUser(member)<span style="font-family:Arial, Helvetica, sans-serif;">;</span>
- if(roles!=null){
- for(RoleModel role : roles) {
- authSet.add(new SimpleGrantedAuthority(role.getRoleCode().trim()));
- }
- }
- return authSet;
- }
- }
6、MyAccessDecisionManager.java
- package cn.com.abel.test.service.security;
- import java.util.Collection;
- import java.util.Iterator;
- import org.springframework.security.access.AccessDecisionManager;
- import org.springframework.security.access.AccessDeniedException;
- import org.springframework.security.access.ConfigAttribute;
- import org.springframework.security.authentication.InsufficientAuthenticationException;
- import org.springframework.security.core.Authentication;
- import org.springframework.security.core.GrantedAuthority;
- public class MyAccessDecisionManager implements AccessDecisionManager {
- public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
- if(configAttributes == null) {
- return;
- }
- //所请求的资源拥有的权限(一个资源对多个权限)
- Iterator<ConfigAttribute> iterator = configAttributes.iterator();
- while(iterator.hasNext()) {
- ConfigAttribute configAttribute = iterator.next();
- //访问所请求资源所需要的权限
- String needPermission = configAttribute.getAttribute();
- System.out.println("needPermission is " + needPermission);
- //用户所拥有的权限authentication
- for(GrantedAuthority ga : authentication.getAuthorities()) {
- if(needPermission.equals(ga.getAuthority())) {
- return;
- }
- }
- }
- //没有权限让我们去捕捉
- throw new AccessDeniedException(" 没有权限访问!");
- }
- public boolean supports(ConfigAttribute attribute) {
- // TODO Auto-generated method stub
- return true;
- }
- public boolean supports(Class<?> clazz) {
- // TODO Auto-generated method stub
- return true;
- }
- }
- package cn.com.abel.test.service.security;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.TreeMap;
- import java.util.concurrent.ConcurrentHashMap;
- import javax.servlet.http.HttpServletRequest;
- import org.apache.commons.lang.StringUtils;
- import org.springframework.beans.factory.InitializingBean;
- import org.springframework.security.access.ConfigAttribute;
- import org.springframework.security.access.SecurityConfig;
- import org.springframework.security.web.FilterInvocation;
- import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
- import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
- import org.springframework.security.web.util.matcher.RequestMatcher;
- import cn.com.abel.test.model.RoleModel;
- import cn.com.abel.test.service.ResourceService;
- public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource,InitializingBean {
- private static final String AUTH_NO_ROLE =" __AUTH_NO_ROLE__";
- private ResourceService resourceService;
- public MySecurityMetadataSource(ResourceService resourceService) {
- this.resourceService = resourceService;
- }
- private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
- public Collection<ConfigAttribute> getAllConfigAttributes() {
- return null;
- }
- public boolean supports(Class<?> clazz) {
- return true;
- }
- private void loadResourceDefine() {
- if(resourceMap == null) {
- resourceMap = new ConcurrentHashMap<String, Collection<ConfigAttribute>>();
- }else{
- resourceMap.clear();
- }
- Map<String,List<RoleModel>> resourceRoleMap = resourceService.getAllResourceRole();
- for (Entry<String,List<RoleModel>> entry : resourceRoleMap.entrySet()) {
- String url = entry.getKey();
- List<RoleModel> values = entry.getValue();
- Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
- for(RoleModel secRoleModel : values){
- ConfigAttribute configAttribute = new SecurityConfig(StringUtils.defaultString(secRoleModel.getRoleCode(),AUTH_NO_ROLE));
- configAttributes.add(configAttribute);
- }
- resourceMap.put(url, configAttributes);
- }
- }
- public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
- HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
- TreeMap<String, Collection<ConfigAttribute>> attrMap = new TreeMap<String, Collection<ConfigAttribute>>(resourceMap);
- Iterator<String> ite = attrMap.keySet().iterator();
- RequestMatcher urlMatcher = null;
- Collection<ConfigAttribute> attrSet = new HashSet<ConfigAttribute>();
- //match all of /admin/** a/b/**
- while (ite.hasNext()) {
- String resURL = ite.next();
- urlMatcher = new AntPathRequestMatcher(resURL);
- if (urlMatcher.matches(request)||StringUtils.equals(request.getRequestURI(),resURL)) {
- attrSet.addAll(attrMap.get(resURL));
- }
- }
- if(!attrSet.isEmpty()){
- return attrSet;
- }
- return null;
- }
- @Override
- public void afterPropertiesSet() throws Exception {
- loadResourceDefine() ;
- }
- }
8、ResourceService.java
此类是为从数据库获取系统中的资源所属的角色,根据自己的数据表自行编写。
- package cn.com.abel.test.service;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Map;
- import org.apache.commons.collections.CollectionUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import cn.com.abel.test.mapper.ResourceModelMapper;
- import cn.com.abel.test.mapper.RoleModelMapper;
- import cn.com.abel.test.mapper.RoleResourcetModelMapper;
- import cn.com.abel.test.model.ResourceModel;
- import cn.com.abel.test.model.ResourceModelCriteria;
- import cn.com.abel.test.model.RoleModel;
- import cn.com.abel.test.model.RoleModelCriteria;
- import cn.com.abel.test.model.RoleResourcetModel;
- import cn.com.abel.test.model.RoleResourcetModelCriteria;
- @Service
- public class ResourceService {
- @Autowired
- ResourceModelMapper resourceModelMapper;
- @Autowired
- RoleModelMapper roleMapper;
- @Autowired
- RoleResourcetModelMapper roleResMapper;
- /**
- * 获取各个资源(url)对应的角色
- * @return
- */
- public Map<String,List<RoleModel>> getAllResourceRole(){
- Map<String,List<RoleModel>> resultMap = new HashMap<String,List<RoleModel>>();
- ResourceModelCriteria secResourceModelExample = new ResourceModelCriteria();
- List<ResourceModel> resourceList = resourceModelMapper.selectByExample(secResourceModelExample);
- if(CollectionUtils.isNotEmpty(resourceList)){
- for(ResourceModel secResourceModel : resourceList){
- RoleModelCriteria roleCriteria = new RoleModelCriteria();
- roleCriteria.createCriteria().andIdIn(getRoleIdsByResourceId(secResourceModel.getId()));
- List<RoleModel> roleList = roleMapper.selectByExample(roleCriteria);
- resultMap.put(secResourceModel.getValue(), roleList);
- }
- }
- return resultMap;
- }
- public List<Integer> getRoleIdsByResourceId(Integer resourceId){
- List<Integer> roleIds = new ArrayList<Integer>();
- RoleResourcetModelCriteria criteria = new RoleResourcetModelCriteria();
- criteria.createCriteria().andResourceIdEqualTo(resourceId);
- List<RoleResourcetModel> list = roleResMapper.selectByExample(criteria);
- if(CollectionUtils.isNotEmpty(list)){
- for(RoleResourcetModel model : list){
- roleIds.add(model.getRoleId());
- }
- }
- HashSet<Integer> h = new HashSet<Integer>(roleIds);
- roleIds.clear();
- roleIds.addAll(h);
- return roleIds;
- }
- }
最后附上数据表的SQL:
- CREATE TABLE `auth_resource` (
- `id` INT(11) NOT NULL AUTO_INCREMENT,
- `name` VARCHAR(100) NULL DEFAULT NULL COMMENT '资源名称',
- `value` VARCHAR(100) NULL DEFAULT NULL COMMENT '资源值',
- `summary` VARCHAR(1000) NULL DEFAULT NULL COMMENT '资源描述',
- `updated_time` DATETIME NULL DEFAULT NULL,
- `updated_user` VARCHAR(100) NULL DEFAULT NULL,
- PRIMARY KEY (`id`)
- )
- COMMENT='资源访问表'
- COLLATE='utf8_general_ci'
- ENGINE=InnoDB;
- CREATE TABLE `auth_role` (
- `id` INT(11) NOT NULL AUTO_INCREMENT,
- `role_name` VARCHAR(100) NULL DEFAULT NULL COMMENT '角色名称',
- `role_code` VARCHAR(100) NULL DEFAULT NULL COMMENT '角色代码',
- `updated_time` DATETIME NULL DEFAULT NULL,
- `updated_user` VARCHAR(100) NULL DEFAULT NULL,
- PRIMARY KEY (`id`)
- )
- COMMENT='角色表'
- COLLATE='utf8_general_ci'
- ENGINE=InnoDB;
- CREATE TABLE `role_resource` (
- `id` INT(11) NOT NULL AUTO_INCREMENT,
- `role_id` INT(11) NOT NULL,
- `resource_id` INT(11) NOT NULL,
- PRIMARY KEY (`id`),
- UNIQUE INDEX `role_id_resource_id` (`role_id`, `resource_id`)
- )
- COMMENT='资源角色关联表'
- COLLATE='utf8_general_ci'
- ENGINE=InnoDB;
- CREATE TABLE `member` (
- `id` INT(11) NOT NULL AUTO_INCREMENT,
- `user_name` VARCHAR(100) NULL DEFAULT NULL,
- `nick` VARCHAR(100) NULL DEFAULT NULL,
- `password` VARCHAR(100) NULL DEFAULT NULL,
- `sex` INT(11) NULL DEFAULT NULL,
- `birthday` DATE NULL DEFAULT NULL,
- `mobile` VARCHAR(50) NULL DEFAULT NULL,
- `email` VARCHAR(50) NULL DEFAULT NULL,
- `address` VARCHAR(512) NULL DEFAULT NULL,
- `regip` VARCHAR(100) NULL DEFAULT NULL,
- `created_time` DATETIME NULL DEFAULT NULL,
- PRIMARY KEY (`id`)
- )
- COMMENT='用户表'
- COLLATE='utf8_general_ci'
- ENGINE=InnoDB
- AUTO_INCREMENT=2;
- CREATE TABLE `member_role` (
- `id` INT(11) NOT NULL AUTO_INCREMENT,
- `member_id` INT(11) NOT NULL,
- `role_id` INT(11) NOT NULL,
- PRIMARY KEY (`id`),
- UNIQUE INDEX `member_id_role_id` (`member_id`, `role_id`)
- )
- COMMENT='用户角色关联表'
- COLLATE='utf8_general_ci'
- ENGINE=InnoDB;
完整代码下载(包含数据库):http://download.csdn.net/download/rongku/9931455