一个zhuo'li自定义Spring 权限框架

权限控制

认证授权

概念

认证:系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录操作其实就是在进行认证,认证的目的是让系统知道是谁

授权:用户认证成功后,需要为用户授权,其实就是指定当前用户可以操作哪些功能

分析

权限控制的基本实现思路:

先通过用户名和密码对用户进行认证,认证就是判断当前用户是否是系统用户

然后再根据用户ID或用户名获取权限信息,即授权信息

授权信息是用户角色及权限标识的集合,把用户的角色,权限标识集合与用户关联,最终放入集合中.

每个用户的授权信息,只需要在登录的时候加载一次即可,所以我们把授权的代码放在登录成功之后执行

认证授权实现

UserService

修改login方法, 添加授权(加载用户的权限)

//判断查询的用户名和密码是否有值

if(user!=null){

//先创建一个list集合,用来存储用户加载的角色

List<String>list=newArraryList<>();

//调用Dao层加载用户拥有的角色

RoleDaoroledao=SqlSessiopn.getMapping(RoleDao.class);

//传入用户的id属性,返回Role集合

List<Role>roleList=roledao.findByUid(user.getId);

//判断该用户是否有权限 (Dao层返回的对象是否有值)

if(roleList!=null&&rolelist.size()>0){

//加载每个用户对应的角色的权限资源

PermissionDaopermission=sqlSession.getMapper(PermissionDao.class);


for (Rolerole:roleList){

//循环将role的keyWord存入list集合

list.add(role.getKeyword());

//调用Dao层,传入roleid作为参数

List<Permission>permissionList=permission.findByRoledId(role.getId);

if  (permissionList!=null){

for (Permissionpermission:permissionList){

list.add(permission.getKeyword());

               }

           }

       }

   }

//将list集合作为参数修改到user中的authorityList集合属性中

user.setAuthorityList(list)



}

RoleDao

List<Role>findByUid(Integerid);//调用映射文件对应的id标签

RoleDao.xml

<selectid="findByUid"parameterType="int"resultType="Role">

select * from t_role where id in(select role_id from tr_user_role where user_id=#{uid})  

</select>

PermissionDao

List<Permission>findByRoleId(Integerid);//调用映射文件对应的id标签

PermissionDao.xml

<mappernamespace="com.itheima.mm.dao.PermissionDao">

<selectid="findByRoleId"parameterType="int"resultType="Permission">

  select * from t_permission where id in(select permission_id from tr_role_permission where role_id=#{roleId})

</select>

自定义权限框架流程

权限控制实现步骤

配置权限资源

如果一个资源,需要指定权限/角色才能访问,那么:要把资源和权限/角色的关系进行配置

可以采用两张配置结合使用:

XML配置 : 用于对一些资源文件(如:html文件)的权限进行配置

注解配置 : 用于对web层方法的权限进行配置

加载配置

当服务器启动时 ,要获取资源所需要的权限配置

拦截请求 ,判断

当前登录的用户拥有的权限,是否包含了访问资源所需要的权限

自定义框架-基于XML

创建配置文件

文件名称:mm-security.xml

文件位置:放在resources目录下

文件说明:

beans:根标签

security标签:一个标签,配置一个资源所需要的角色

pattern:资源路径,这里配置的是html页面的路径

has_role:资源所需要的角色。多个角色之间,用逗号隔开

表示:访问pattern资源,用户必须有has_role中定义的角色之一;否则无权限

<?xmlversion="1.0" encoding="UTF-8" ?>

<beans>

<securitypattern="/pages/index.html"has_role="ROLE_ADMIN,ROLE_QUESTION_RECORDER"/>

<securitypattern="/pages/questionBasicList.html"has_role="ROLE_ADMIN,ROLE_QUESTION_RECORDER"/>

<securitypattern="/pages/questionClassicList.html"has_role="ROLE_ADMIN"/>

<securitypattern="/pages/userList.html"has_role="ROLE_ADMIN"/>

<!--注解扫描包。配置web层的包名-->

<scanpackage="<!--自定义注解所在的包名-->"/>

</beans>

创建过滤器

publicclassSecurityFilterimplementsFilter{

//创建一个map集合用来存存储配置文件中的pattern属性(键) 和 has_role属性(值)

privateMap<String,String>map=newHashMap<>();

@Override

publicvoidinit(FilterConfigfilterConfig)throwsServletException{

//需要加载配置文件mm-security.xml 得到每个页面资源可以访问的角色

InputStreaminputStream=null;

try{

//通过过滤器的FilterConfig对象,可以得到web.xml里,配置的初始化参数

StringconfigLocation=filterConfig.getInitParameter("configLocation");

inputStream=this.getClass().getClassLoader().getResourceAsStream(configLocation);

//读取解析xml文件,得到里边的配置  使用的方法:dom4j

SAXReaderreader=newSAXReader();

Documentdocument=reader.read(inputStream);

/*  方法一

//获取xml文件里所有的security标签

//先找到根标签 ,再根据根标签找到里边的子标签

Element rootElement=document.getRootElement();

rootElement.element("security");*/

//方法二  用xpath表达式 //security,表示从xml文件里全文搜索security标签

List<Element>security=document.selectNodes("//security");

for(ElementsecurityElement:security){

//获取security标签的pattern属性值:资源路径

Stringpattern=securityElement.attributeValue("pattern");

//获取security标签的has_role属性值:哪些角色可以访问

Stringhas_role=securityElement.attributeValue("has_role");

//将读取的数据存储到Map集合,以资源路径作为键,以has_role的属性值作为值

map.put(pattern,has_role);

           }

}catch(Exceptione) {

e.printStackTrace();

}finally{

try{

if(inputStream!=null) {

//关闭输入流

inputStream.close();

               }

}catch(IOExceptione) {

e.printStackTrace();

           }

       }

   }

/**

* 权限过滤: 判断用户拥有的权限,能否访问目标资源

* @param request

* @param response

* @param chain

*/

@Override

publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{

HttpServletRequestreq=(HttpServletRequest)request;

HttpServletResponseres=(HttpServletResponse)response;

//获取客户端请求的资源是,去掉项目资源和无关后缀

Stringur=req.getRequestURI();

StringcontextPath=req.getContextPath();

Stringuri=ur.substring(contextPath.length());//去掉项目路径

if(uri.endsWith(".do")){//判断如果结尾是.do,则替换成""

uri=uri.replace(".do","");

       }

//从map里获取访问资源所需要的角色(权限)

StringhasRole=map.get(uri);

//如果客户端请求的页面在xml文件中没有配置资源路径,则直接放行

if(hasRole==null){

//不需要进行权限控制

System.out.println(uri+"不需要权限控制.直接放行");

chain.doFilter(req,res);

return;

       }

//获取当前用户拥有的角色(权限)

Useruser=(User)req.getSession().getAttribute("user");

//如果没有登录

if(user==null){

System.out.println(uri+"访问的用户没有登录");

res.sendRedirect(contextPath+"/login.html");

return;

       }

//已登录 得到当前用户拥有的权限

List<String>authorityList=user.getAuthorityList();

//如果当前用户

if(authorityList==null||authorityList.size()==0){

res.getWriter().print(user.getUsername()+"用户无权限");

System.out.println(user.getUsername()+"用户没有权限");

return;

       }

booleancanAccess=false;//定义一个布尔变量  旗帜变量

//判断用户的权限,是否足够   用户拥有的角色(权限)里,是否包含目标资源所需要的任何一个角色(权限)

//用逗号进行分割  [ROLE_ADMIN],[ROLE_QUESTION_RECORDER]

String[]split=hasRole.split(",");

for(Strings:split){

//如果数据表中包含这个权限

if(authorityList.contains(s)){

canAccess=true;

break;

           }

       }

if(canAccess){

System.out.println(uri+"权限足够,放行");

chain.doFilter(req,res);

}else{

System.out.println(uri+"当前用户"+user.getUsername()+"权限不足,请切换用户");

res.getWriter().print("<<路径:"+uri+">>\n当前用户"+user.getUsername()+",权限不足,请切换用户");

       }

   }

过滤器配置(web.xml)

为了提高程序的可用性,解决硬编码问题,我们采用web.xml配置过滤器

<!--配置权限过滤器-->

<filter>

<filter-name><!--过滤器的名称--></filter-name>

<filter-class><!--过滤器的所在的全限定类名--></filter-class>

<!--初始化参数-->

<init-param>

<param-name><!--定义一个名称--></param-name>

<param-value><!--过滤器配置文件名.xml--></param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name><!--过滤器名称--></filter-name>

<url-pattern>/*</url-pattern><!--过滤的范围-->

</filter-mapping>

自定义框架-基于注解

创建注解

@Target(ElemenType.METHOD)          //元注解: 注解定义的位置

@Retention(RetentionPolicy.RUNTIME)//元注解: 注解存活的期限

public@interfacePreAuthorize{

Stringvalue();

}

在控制器方法上增加注解配置权限

    @PreAuthorize("QUESTION_REVIEW_UPDATE")

    @RequestMapping("/review/reviewQuestion")

publicvoidreviewQuestion(HttpServletRequestrequest,HttpServletResponseresponse)throwsIOException{

//......内容省略了.......重点看方法上增加的注解

  }

3.在过滤器中的int方法里增加代码

privateMap<String,String>map=newHashMap<>();

@Override

publicvoidinit(FilterConfigfilterConfig)throwsServletException{

//需要加载配置文件mm-security.xml 得到每个页面资源可以访问的角色

InputStreaminputStream=null;

try{

//通过过滤器的FilterConfig对象,可以得到web.xml里,配置的初始化参数

StringconfigLocation=filterConfig.getInitParameter("configLocation");

inputStream=this.getClass().getClassLoader().getResourceAsStream(configLocation);

//读取解析xml文件,得到里边的配置  dom4j

SAXReaderreader=newSAXReader();

Documentdocument=reader.read(inputStream);

/*  方法一

//获取xml文件里所有的security标签

//先找到根标签 ,再根据根标签找到里边的子标签

Element rootElement=document.getRootElement();

rootElement.element("security");*/

//方法二  用xpath表达式 //security,表示从xml文件里全文搜索security标签

List<Element>security=document.selectNodes("//security");

for(ElementsecurityElement:security){

//获取security标签的pattern属性值:资源路径

Stringpattern=securityElement.attributeValue("pattern");

//获取security标签的has_role属性值:哪些角色可以访问

Stringhas_role=securityElement.attributeValue("has_role");

//将读取的数据存储到Map集合,以资源路径作为键,以has_role的属性值作为值

map.put(pattern,has_role);

           }

//扫描Controller类中的方法  得到:资源 权限 是否可以访问

//先扫描xml文件中的package标签,得到里面的值

ElementscanEelement=(Element)document.selectSingleNode("//scan");

StringPackageName=scanEelement.attributeValue("package");

//扫描指定包下所有的类

List<Class<?>>classsFromPackage=    ClassScannerUtils.getClasssFromPackage(PackageName);

if(classsFromPackage!=null&&classsFromPackage.size()>0) {

//循环这个包下所有的类

for(Class<?>aClass:classsFromPackage) {

//先判断这个类是不是控制器

booleanannotationPresent=aClass.isAnnotationPresent(Controller.class);

if(annotationPresent){

//获取该类中的全部方法

Method[]methods=aClass.getMethods();

for(Methodmethod:methods){

//扫描类中所有方法,判断是否有@RequestMapping注解

booleanisRequestMapping=        method.isAnnotationPresent(PreAuthorize.class);

if(isRequestMapping){

//得到资源的访问路径

RequestMappingannotation=method.getAnnotation(RequestMapping.class);

StringmappingPath=annotation.value();

//得到资源的权限

PreAuthorizepreAuthorize=method.getAnnotation(PreAuthorize.class);

Stringvalue=preAuthorize.value();

//将这些数据保存到map中 以访问路径做键(@RequestMapping的值) 以@PreAuthorize的值为值

map.put(mappingPath,value);

                           }

                       }

                   }

               }

           }

}catch(Exceptione) {

e.printStackTrace();

}finally{

try{

if(inputStream!=null) {

inputStream.close();

               }

}catch(IOExceptione) {

e.printStackTrace();

           }

       }

   }

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容