今天有个群友问了下ss3的问题,他主要是登陆实现权限的校验的顺序不清晰,当初我学这个框架的时候也是被ss3登陆校验权限的顺序困扰了几天。下面是我一点理解,有错的话还望指正一下。


其实,要实现自定义权限的话只有几个关键的类和接口,只要搞清楚他们的顺序就行了。

1、你可以初始化话容器的时候就加载资源和权限列表,这个在实现FilterInvocationSecurityMetadataSource接口的类里,定一个全局的静态map,以url为key,所需权限(集合)为value,装进这个静态map。


2、登陆。登陆的action在spring配置文件里配好。这个action会直接到继承UsernamePasswordAuthenticationFilter类的一个重写的attemptAuthentication方法里,把你输入username和password(加入配了MD5加密security3会自动帮你加密)和数据比对,如果存在这个username和password,进入第3步(这是后没有退出这个attemptAuthentication方法)。没有这个username和password的话直接抛异常和终止此次登陆。


3、如果username和password存在,会进入到实现UserDetailsService接口的类里的一个重写的loadUserByUsername方法,这个方法里会把该username所有拥有的权限设为安全权限然后返回一个UserDetails类型的结果。这是返回到第二步中的attemptAuthentication中,把这这个认证了得安全实体有所拥有的权限以Authentication类型返回。共第五步调用。


4、登陆认证成功后会有一个action(这个action在spring配置文件里配置),这个action是第一个url权限认证(假设这个action需要权限认证),这个请求会进入到实现了FilterInvocationSecurityMetadataSource接口的类里的一个重写的getAttributes方法,将第1步中的资源权限列表map中get出来,返回Collection<ConfigAttribute>类型的结果,共第5部调用。


5,第4步执行完之后会进入实现类AccessDecisionManager接口的类的一个重写decide方法,在这个方法中会将第3步中返回的Authentication和第4步中返回的Collection<ConfigAttribute>进行比对。比对成功则可以访问,比对不成功会跳转一个页面(这个页面也是在spring配置文件中配置)。


至此,已经完成了登陆的验证并成功执行一个action。


当然,你可以在登陆成功后添加一些你自己业务,比如说记录登陆次数和登陆ip,这需要实现security3的一些接口,并配置在spring配置文件里。


下面是所用到的一些关键的类

1、实现了FilterInvocationSecurityMetadataSource接口的类

/**
* @Description : 描述
* @author YangXuan
*@email 364105996@qq.com
* @date Aug 6, 2013 8:56:44 PM
*/
public class MySecurityMetadataSource implements
		FilterInvocationSecurityMetadataSource {

	public MySecurityMetadataSource(ResourcesDao resourcesDao, RolesDao rolesDao) {
		this.resourcesDao = resourcesDao;
		this.rolesDao = rolesDao;
		this.loadResourceDefine();
	}

	private ResourcesDao resourcesDao;
	private RolesDao rolesDao;
	private RequestMatcher requestMatcher;
	private String matcher = "ant";

	public ResourcesDao getResourcesDao() {
		return resourcesDao;
	}

	public void setResourcesDao(ResourcesDao resourcesDao) {
		this.resourcesDao = resourcesDao;
	}

	public RolesDao getRolesDao() {
		return rolesDao;
	}

	public void setRolesDao(RolesDao rolesDao) {
		this.rolesDao = rolesDao;
	}

	public void setRequestMatcher(RequestMatcher requestMatcher) {
		this.requestMatcher = requestMatcher;
	}

	public void setMatcher(String matcher) {
		this.matcher = matcher;
	}

	// 返回所请求资源所需要的权限
	public Collection<ConfigAttribute> getAttributes(Object object)
			throws IllegalArgumentException {
		HttpServletRequest request = ((FilterInvocation) object).getRequest();
		String requestUrl = ((FilterInvocation) object).getRequestUrl();
		System.out.println("requestUrl is " + requestUrl);
		if (resourceMap == null) {
			loadResourceDefine();
		}

		Set<String> urlMatch = resourceMap.keySet();
		for (String url : urlMatch) {
			if (matcher.toLowerCase().equals("ant")) {
				requestMatcher = new AntPathRequestMatcher(url);
			}
			if (matcher.toLowerCase().equals("regex")) {
				requestMatcher = new RegexRequestMatcher(url,
						request.getMethod(), true);
			}
			if (requestMatcher.matches(request)) {
				return resourceMap.get(url);
			}
		}
		return null;
	}

	public Collection<ConfigAttribute> getAllConfigAttributes() {
		return null;
	}

	public boolean supports(Class<?> clazz) {
		return true;
	}

	public static Map<String, Collection<ConfigAttribute>> resourceMap = null;

	private void loadResourceDefine() {
		System.out
				.println(">>>>>>>>>>loadResourceDefine()---successfully<<<<<<<<<<");
		if (resourceMap == null) {
			resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
			List<Resources> resources = this.resourcesDao.findAllResources();
			for (Resources resource : resources) {
				/*System.out.println(resource.getId() + "---" + resource.getUrl()
						+ "---" + resource.getDescr() + "---"
						+ resource.isEnabled());*/
				List<Roles> roles = this.rolesDao
						.findRolesByResourcesId(resource.getId());
				Collection<ConfigAttribute> configAttributes = new HashSet<ConfigAttribute>();
				for (Roles role : roles) {
					configAttributes
							.add(new SecurityConfig(role.getRoleName()));
				}
				resourceMap.put(resource.getUrl(), configAttributes);
			}
		}
	}
}


2、继承了UsernamePasswordAuthenticationFilter的类

/**
* @Description : 描述
* @author YangXuan
*@email 364105996@qq.com
* @date Aug 6, 2013 8:57:45 PM
*/
public class MyUsernamePasswordAuthenticationFilter extends
		UsernamePasswordAuthenticationFilter {
	// 定义从前台接收参数的 属性名称
	private String validationParameter = "validation";

	public void setValidationParameter(String validationParameter) {
		this.validationParameter = validationParameter;
	}

	private boolean openValidation = true;

	public void setOpenValidation(boolean openValidation) {
		this.openValidation = openValidation;
	}

	@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).trim();
		String password = obtainPassword(request).trim();

		// 验证码validation是否正确
		if (openValidation) {
			checkValidateCode(request);
		}

		// 实现 Authentication,这里装进去的password会通过spring的MD5加密,然后实现校验
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);
		// 允许子类设置详细属性
		setDetails(request, authRequest);

		// 运行UserDetailsService的loadUserByUsername 再次封装Authentication
		return this.getAuthenticationManager().authenticate(authRequest);
	}

	@Override
	protected String obtainUsername(HttpServletRequest request) {
		Object obj = request.getParameter(getUsernameParameter());
		return null == obj ? "" : obj.toString();
	}

	@Override
	protected String obtainPassword(HttpServletRequest request) {
		Object obj = request.getParameter(getPasswordParameter());
		return null == obj ? "" : obj.toString();
	}

	protected String obtainValidationString(HttpServletRequest request) {
		Object obj = request.getParameter(validationParameter);
		return null == obj ? "" : obj.toString();
	}

	public void checkValidateCode(HttpServletRequest request) {

		String jcaptchaCode = obtainValidationString(request).trim()
				.toUpperCase();		//获取前台的验证码输入值
		if (null == jcaptchaCode || jcaptchaCode.equals(""))
			throw new BadCredentialsException("验证码超时!!!");

		boolean b = CaptchaServiceSingleton.getInstance()
				.validateResponseForID(request.getSession().getId(),
						jcaptchaCode);
		if (!b)
			throw new BadCredentialsException("验证码不匹配!!!");
	}

}

3、实现了UserDetailsService接口的类

/**
* @Description : 描述
* @author YangXuan
*@email 364105996@qq.com
* @date Aug 6, 2013 8:58:30 PM
*/
public class MyUserDetailServiceImpl implements UserDetailsService {

	private UsersDao usersDao;
	private RolesDao rolesDao;

	public UsersDao getUsersDao() {
		return usersDao;
	}

	public void setUsersDao(UsersDao usersDao) {
		this.usersDao = usersDao;
	}

	public RolesDao getRolesDao() {
		return rolesDao;
	}

	public void setRolesDao(RolesDao rolesDao) {
		this.rolesDao = rolesDao;
	}

	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException {

		Users users = this.usersDao.findByName(username);
		Set<GrantedAuthority> grantedAuths = obtionGrantedAuthorities(users);
		users.setAuthorities(grantedAuths);

		return users;
	}

	private Set<GrantedAuthority> obtionGrantedAuthorities(Users user) {
		Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();
		List<Roles> roles = this.rolesDao.findRolesByUsersId(user.getId());
		for (Roles role : roles) {
			grantedAuthorities.add(new SimpleGrantedAuthority(role
					.getRoleName()));
		}
		return grantedAuthorities;
	}
}

4、实现了AccessDecisionManager接口的类

/**
* @Description : 描述
* @author YangXuan
*@email 364105996@qq.com
* @date Aug 6, 2013 8:59:02 PM
*/
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();
			for (GrantedAuthority ga : authentication.getAuthorities()) {
				if (needPermission.equals(ga.getAuthority())) {
					return;
				}
			}
		}
		// 没有权限让我们去捕捉
		throw new AccessDeniedException(" No authority to access!");
	}

	public boolean supports(ConfigAttribute attribute) {
		return true;
	}

	public boolean supports(Class<?> clazz) {
		return true;
	}

}

敲这些文字敲得好辛苦啊……不喜勿喷哈

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐