spring security3 实现自定义管理权限

来源:互联网 发布:肯德基 网络市场调研 编辑:程序博客网 时间:2024/06/06 11:05


今天有个群友问了下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 implementsFilterInvocationSecurityMetadataSource {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 extendsUsernamePasswordAuthenticationFilter {// 定义从前台接收参数的 属性名称private String validationParameter = "validation";public void setValidationParameter(String validationParameter) {this.validationParameter = validationParameter;}private boolean openValidation = true;public void setOpenValidation(boolean openValidation) {this.openValidation = openValidation;}@Overridepublic 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 再次封装Authenticationreturn this.getAuthenticationManager().authenticate(authRequest);}@Overrideprotected String obtainUsername(HttpServletRequest request) {Object obj = request.getParameter(getUsernameParameter());return null == obj ? "" : obj.toString();}@Overrideprotected 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;}}

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