spring security自定义登陆和动态
maven的jar包引入在spring security环境的构建中已经介绍过了.web.xml的配置也不变,不知道的小伙伴可以返回去看下配置的博客。下面我们就直接切入正题.
1.修改spring_security.xml的配置文件
<?xml version="1.0" encoding="UTF-8"?>
2.创建相关的类
这里我将会依据配置文件中类的引入顺序给大家加入类。
1.自定义过滤器类
//自定义拦截器类
/**
* 通过MyUserDetailService拿到用户信息后,authenticationManager对比用户的密码(即验证用户),
* 然后这个AuthenticationProcessingFilter拦截器就过咯。
* */
/**
* 首先,登陆后,每次访问资源都会被这个拦截器拦截,会执行doFilter这个方法,这个方法调用了invoke方法,
* 其中fi断点显示是一个url(可能重写了toString方法吧,但是里面还有一些方法的),最重要的是
* beforeInvocation这个方法,它首先会调用MyInvocationSecurityMetadataSource类的
* getAttributes方法获取被拦截url所需的权限,在调用MyAccessDecisionManager类decide
* 方法判断用户是否够权限。弄完这一切就会执行下一个拦截器。
* */
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
//配置文件注入
private FilterInvocationSecurityMetadataSource securityMetadataSource;
//登陆后,每次访问资源都通过这个拦截器拦截
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException \{
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
\}
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() \{
return this.securityMetadataSource;
\}
public Class<? extends Object> getSecureObjectClass() \{
return FilterInvocation.class;
\}
/\*\*
\* @param fi FilterInvocation把doFilter传进来的request,response和FilterChain对象保存起来,供FilterSecurityInterceptor的处理代码调用。
\* 好处:使得代码的可阅读性和藕合性大大降低,因为FilterInvocation类替代了这些参数在FilterSecurityInterceptor类中各处游动,,这样通过该类屏蔽了web filter过滤器环境。
\* \*/
public void invoke(FilterInvocation fi) throws IOException, ServletException \{
//fi里面有一个被拦截的url
//里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
//再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
InterceptorStatusToken token = super.beforeInvocation(fi);
try \{
//执行下一个拦截器
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
\} finally \{
super.afterInvocation(token, null);
\}
\}
public SecurityMetadataSource obtainSecurityMetadataSource() \{
return this.securityMetadataSource;
\}
public void setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource newSource)
\{
this.securityMetadataSource = newSource;
\}
public void destroy() \{
\}
public void init(FilterConfig arg0) throws ServletException \{
\}
}
2.用户管理类,主要用来获取用户的相关信息
public class MyUserDetailService implements UserDetailsService {
// 登陆验证时,通过username获取用户的所有权限信息,
// 并返回User放到spring的全局缓存SecurityContextHolder中,以供授权器使用
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
Collection
if (username.equals(“lcy”)) {
auths = new ArrayList
GrantedAuthorityImpl auth2 = new GrantedAuthorityImpl(“ROLE_ADMIN”);
GrantedAuthorityImpl auth1 = new GrantedAuthorityImpl(“ROLE_USER”);
auths.add(auth1);
auths.add(auth2);
}
User user = new User(username, “lcy”, true, true, true, true, auths);
return user;
}
}
3.决策器类,决定某个用户具有的角色,是否有足够的权限去访问某个资源
public class MyAccessDecisionManager implements AccessDecisionManager \{
// 检查用户是否够权限访问资源
// 参数authentication是从spring的全局缓存SecurityContextHolder中拿到的,里面是用户的权限信息
// 参数object是url
// 参数configAttributes所需的权限
public void decide(Authentication authentication, Object object, Collection
throws AccessDeniedException, InsufficientAuthenticationException {
if (configAttributes == null) {
return;
}
Iterator
while (ite.hasNext()) {
ConfigAttribute ca = ite.next();
String needRole = ((SecurityConfig) ca).getAttribute();
for (GrantedAuthority ga : authentication.getAuthorities()) {
if (needRole.equals(ga.getAuthority())) {
return;
}
}
}
// 注意:执行这里,后台是会抛异常的,但是界面会跳转到所配的access-denied-page页面
throw new AccessDeniedException(“no right”);
}
public boolean supports(ConfigAttribute attribute) {
return true;
}
public boolean supports(Class<?> clazz) {
return true;
}
}
4.资源源数据定义类,将所有的资源和权限对应关系建立起来
/**
* 主要是用来初始化类和路径之间的关系
* */
public class MyInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private UrlMatcher urlMatcher = new AntUrlPathMatcher();
private static Map
// tomcat启动时实例化一次
public MyInvocationSecurityMetadataSource() {
loadResourceDefine();
}
// tomcat开启时加载一次,加载所有url和权限(或角色)的对应关系
/**
* loadResourceDefine方法不是必须的,这个只是加载所有的资源与权限的对应关系并缓存起来,避免每次获取权限都访问数据库(提高性能),然后getAttributes根据参数(被拦截url)返回权限集合。
* */
private void loadResourceDefine() {
resourceMap = new HashMap
Collection
ConfigAttribute ca = new SecurityConfig(“ROLE_USER”);
atts.add(ca);
resourceMap.put(“/index.jsp”, atts);
Collection
ConfigAttribute cano = new SecurityConfig(“ROLE_NO”);
attsno.add(cano);
resourceMap.put(“/other.jsp”, attsno);
}
// 参数是要访问的url,返回这个url对于的所有权限(或角色)
public Collection
// 将参数转为url
String url = ((FilterInvocation) object).getRequestUrl();
Iterator
while (ite.hasNext()) {
String resURL = ite.next();
if (urlMatcher.pathMatchesUrl(resURL, url)) {
return resourceMap.get(resURL);
}
}
return null;
}
public boolean supports(Class<?> clazz) {
return true;
}
public Collection
return null;
}
}
5.可能会用到的相关类
public interface UrlMatcher \{
Object compile(String paramString);
boolean pathMatchesUrl(Object paramObject, String paramString);
String getUniversalMatchPattern();
boolean requiresLowerCaseUrl();
}
public class AntUrlPathMatcher implements UrlMatcher \{
private boolean requiresLowerCaseUrl;
private PathMatcher pathMatcher;
public AntUrlPathMatcher() {
this(true);
}
public AntUrlPathMatcher(boolean requiresLowerCaseUrl) {
this.requiresLowerCaseUrl = true;
this.pathMatcher = new AntPathMatcher();
this.requiresLowerCaseUrl = requiresLowerCaseUrl;
}
public Object compile(String path) {
if (this.requiresLowerCaseUrl) {
return path.toLowerCase();
}
return path;
}
public void setRequiresLowerCaseUrl(boolean requiresLowerCaseUrl) {
this.requiresLowerCaseUrl = requiresLowerCaseUrl;
}
public boolean pathMatchesUrl(Object path, String url) {
if ((“/**“.equals(path)) || (“**“.equals(path))) {
return true;
}
return this.pathMatcher.match((String) path, url);
}
public String getUniversalMatchPattern() {
return “/**“;
}
public boolean requiresLowerCaseUrl() {
return this.requiresLowerCaseUrl;
}
public String toString() {
return super.getClass().getName() + “[requiresLowerCase=’” + this.requiresLowerCaseUrl + “‘]“;
}
}
3.相关页面
这里我就给大家分享下登陆的页面和登陆成功后的跳转页面
登陆界面(login.jsp)
<%@ page language=”java” contentType=”text/html; charset=utf-8”
pageEncoding=”utf-8”%>
<!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd">
用户登录
登陆成功界面(index.jsp)
<%@ page language=”java” contentType=”text/html; charset=utf-8”
pageEncoding=”utf-8”%>
<%@ taglib prefix=”sec”
uri=”http://www.springframework.org/security/tags"%>
<!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd">
这是首页
欢迎
进入admin页面
进入其它页面
总结:
好了,这就是创建自己的自定义登陆界面和动态权限的过程,但是在权限部分我并没有连接数据库动态查询,感兴趣的小伙伴是只要稍作处理就可以实现,理解思想才是最重要的。下面给大家分享下在这个过程中的两个困扰很久问题,大家就不要像我一样走歪路了。
异常:
1.程序加载时的异常:这个其实大部分都是jar包冲突了,主要是版本不匹配,可以统一一下版本
2.页面引入标签的异常:把标签对应的tld文件放置到web-inf下面不然的话可能会加载失败,这个当初困扰了很久
![Center][]
还没有评论,来说两句吧...