spring security自定义登陆和动态

淡淡的烟草味﹌ 2022-06-08 03:54 252阅读 0赞

maven的jar包引入在spring security环境的构建中已经介绍过了.web.xml的配置也不变,不知道的小伙伴可以返回去看下配置的博客。下面我们就直接切入正题.

1.修改spring_security.xml的配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>









































2.创建相关的类

这里我将会依据配置文件中类的引入顺序给大家加入类。

1.自定义过滤器类

  1. //自定义拦截器类

/**
* 通过MyUserDetailService拿到用户信息后,authenticationManager对比用户的密码(即验证用户),
* 然后这个AuthenticationProcessingFilter拦截器就过咯。
* */

/**
* 首先,登陆后,每次访问资源都会被这个拦截器拦截,会执行doFilter这个方法,这个方法调用了invoke方法,
* 其中fi断点显示是一个url(可能重写了toString方法吧,但是里面还有一些方法的),最重要的是
* beforeInvocation这个方法,它首先会调用MyInvocationSecurityMetadataSource类的
* getAttributes方法获取被拦截url所需的权限,在调用MyAccessDecisionManager类decide
* 方法判断用户是否够权限。弄完这一切就会执行下一个拦截器。
* */
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

  1. //配置文件注入
  2. private FilterInvocationSecurityMetadataSource securityMetadataSource;
  3. //登陆后,每次访问资源都通过这个拦截器拦截
  4. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException \{
  5. FilterInvocation fi = new FilterInvocation(request, response, chain);
  6. invoke(fi);
  7. \}
  8. public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() \{
  9. return this.securityMetadataSource;
  10. \}
  11. public Class<? extends Object> getSecureObjectClass() \{
  12. return FilterInvocation.class;
  13. \}
  14. /\*\*
  15. \* @param fi FilterInvocationdoFilter传进来的request,responseFilterChain对象保存起来,供FilterSecurityInterceptor的处理代码调用。
  16. \* 好处:使得代码的可阅读性和藕合性大大降低,因为FilterInvocation类替代了这些参数在FilterSecurityInterceptor类中各处游动,,这样通过该类屏蔽了web filter过滤器环境。
  17. \* \*/
  18. public void invoke(FilterInvocation fi) throws IOException, ServletException \{
  19. //fi里面有一个被拦截的url
  20. //里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
  21. //再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
  22. InterceptorStatusToken token = super.beforeInvocation(fi);
  23. try \{
  24. //执行下一个拦截器
  25. fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
  26. \} finally \{
  27. super.afterInvocation(token, null);
  28. \}
  29. \}
  30. public SecurityMetadataSource obtainSecurityMetadataSource() \{
  31. return this.securityMetadataSource;
  32. \}
  33. public void setSecurityMetadataSource(
  34. FilterInvocationSecurityMetadataSource newSource)
  35. \{
  36. this.securityMetadataSource = newSource;
  37. \}
  38. public void destroy() \{
  39. \}
  40. public void init(FilterConfig arg0) throws ServletException \{
  41. \}

}

2.用户管理类,主要用来获取用户的相关信息

public class MyUserDetailService implements UserDetailsService {
// 登陆验证时,通过username获取用户的所有权限信息,

// 并返回User放到spring的全局缓存SecurityContextHolder中,以供授权器使用
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
Collection auths = null;
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.决策器类,决定某个用户具有的角色,是否有足够的权限去访问某个资源

  1. public class MyAccessDecisionManager implements AccessDecisionManager \{

// 检查用户是否够权限访问资源
// 参数authentication是从spring的全局缓存SecurityContextHolder中拿到的,里面是用户的权限信息
// 参数object是url
// 参数configAttributes所需的权限
public void decide(Authentication authentication, Object object, Collection configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
if (configAttributes == null) {
return;
}

Iterator ite = configAttributes.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> resourceMap = null;

// tomcat启动时实例化一次
public MyInvocationSecurityMetadataSource() {
loadResourceDefine();
}

// tomcat开启时加载一次,加载所有url和权限(或角色)的对应关系
/**
* loadResourceDefine方法不是必须的,这个只是加载所有的资源与权限的对应关系并缓存起来,避免每次获取权限都访问数据库(提高性能),然后getAttributes根据参数(被拦截url)返回权限集合。
* */
private void loadResourceDefine() {
resourceMap = new HashMap>();
Collection atts = new ArrayList();
ConfigAttribute ca = new SecurityConfig(“ROLE_USER”);
atts.add(ca);
resourceMap.put(“/index.jsp”, atts);
Collection attsno = new ArrayList();
ConfigAttribute cano = new SecurityConfig(“ROLE_NO”);
attsno.add(cano);
resourceMap.put(“/other.jsp”, attsno);
}

// 参数是要访问的url,返回这个url对于的所有权限(或角色)
public Collection getAttributes(Object object) throws IllegalArgumentException {
// 将参数转为url
String url = ((FilterInvocation) object).getRequestUrl();
Iterator ite = resourceMap.keySet().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 getAllConfigAttributes() {
return null;
}
}

5.可能会用到的相关类

  1. public interface UrlMatcher \{

Object compile(String paramString);
boolean pathMatchesUrl(Object paramObject, String paramString);
String getUniversalMatchPattern();
boolean requiresLowerCaseUrl();

}

  1. 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">




Insert title here


用户登录
















用户:
密码:





登陆成功界面(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">




Insert title here


这是首页


欢迎

进入admin页面


进入其它页面


总结:

好了,这就是创建自己的自定义登陆界面和动态权限的过程,但是在权限部分我并没有连接数据库动态查询,感兴趣的小伙伴是只要稍作处理就可以实现,理解思想才是最重要的。下面给大家分享下在这个过程中的两个困扰很久问题,大家就不要像我一样走歪路了。

异常:

1.程序加载时的异常:这个其实大部分都是jar包冲突了,主要是版本不匹配,可以统一一下版本

2.页面引入标签的异常:把标签对应的tld文件放置到web-inf下面不然的话可能会加载失败,这个当初困扰了很久

  1. ![Center][]

发表评论

表情:
评论列表 (有 0 条评论,252人围观)

还没有评论,来说两句吧...

相关阅读