在项目中使用Spring Security进行权限控制

1:导入Spring Security环境
(1)pom.xml中添加依赖
(2)web.xml添加代理过滤器
2:实现认证和授权
(1)认证:SpringSecurityUserService.java
(2)创建Service类、Dao接口类、Mapper映射文件
(3)springmvc.xml(dubbo注解扫描范围扩大)
(4)spring-security.xml
(5)springmvc.xml(导入spring-security.xml)
(6)TravelItemController类(@PreAuthorize("hasAuthority('CHECKITEM_ADD')"):完成权限)
(7)travelitem.html(如果没有权限,可以提示错误信息)
(8)导入login.html测试登录
3:显示用户名
4:用户退出

1.1导入Spring Security环境

【路径】
1:pom.xml导入坐标
2:web.xml添加代理过滤器

1.1.1.第一步:pom.xml导入坐标

在父工程的pom.xml中导入Spring Security的maven坐标

            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-web</artifactId>
                <version>${spring.security.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-config</artifactId>
                <version>${spring.security.version}</version>
            </dependency>

1.1.2.第二步:web.xml添加代理过滤器

在meinian_web工程的web,xml文件中配置用于整合Spring Security框架的过滤器DelegatingFilterProxy

    <filter>
        <!--
          DelegatingFilterProxy用于整合第三方框架(代理过滤器,非真正的过滤器,真正的过滤器需要在spring的配置文件)
          整合Spring Security时过滤器的名称必须为springSecurityFilterChain,
          否则会抛出NoSuchBeanDefinitionException异常
        -->
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

1.1.3.第三步:sql语句

#1 使用登录名查询用户信息
SELECT * FROM t_user WHERE username='admin'
#2 传递用户id查询角色合集
SELECT r.* FROM t_role r, t_user_role ur WHERE ur.role_id = r.id AND ur.user_id=1
# 传递角色id查询权限集合
SELECT p.* FROM t_permission p, t_role_permisson rp WHERE p.id = rp.permission_id AND rp.role_id = 1

实现认证和授权

1.2.1.第一步:SpringSecurityUserService.java

在meinian_web工程中按照Spring Security框架要求提供SpringSecurityUserService,并且实现UserDetailsSercice接口

package com.atguigu.security;

import com.alibaba.dubbo.config.annotation.Reference;
import com.atguigu.pojo.Permission;
import com.atguigu.pojo.Role;
import com.atguigu.service.UserService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.Set;

@Component 
public class SpringSecurityUserService implements UserDetailsService {

    @Reference
    UserService userService; // 远程调用

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 1.查询用户信息,以及用户对应的角色,以及角色对应的权限
        com.atguigu.pojo.User user = userService.findUserByUserName(username);
        if (user==null){
            // 不存在这个用户
            return null;// 返回null给框架,框架会抛异常处理,跳转到登录页面
        }
        // 2.构建权限集合
        Set<GrantedAuthority> authorities = new HashSet<>();

        Set<Role> roles = user.getRoles(); //集合数据 由RoleDao帮忙方法来查询得到的
        for (Role role : roles) {
            Set<Permission> permissions = role.getPermissions(); // 集合数据 有 PermissionDao帮忙方法查询得到的
            for (Permission permission : permissions) {
                authorities.add(new SimpleGrantedAuthority(permission.getKeyword()));
            }
        }
        org.springframework.security.core.userdetails.User securityUser = 
                new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
        
        return securityUser; // 框架提供的User实现了UserDetails接口
    }
}

1.2.2.第二步:Service、Dao接口、Mapper映射文件

创建UserService服务接口、服务实现类、Dao接口、Mapper映射文件
【路径】
1:UserService.java 接口
2:UserServiceImpl.java 类
3:UserDao.java(使用用户id查询用户)
4:RoleDao.java (使用用户id查询角色集合)
5:PermissionDao.java(使用角色id查询权限集合)
6:UserDao.xml(使用用户id查询用户)
7:RoleDao.xml(使用用户id查询角色集合)
8:PermissionDao.xml (使用角色id查询权限集合)

使用debug跟踪调试,查看user


ad539466-82bf-4461-84ba-01f1a0e6b2f1.jpg

1.2.3.第三步:springmvc.xml

修改meinian_web工程中的springmvc.xml文件,修改dubbo批量扫描的包路径
之前的扫描包

        <!--批量扫描 -->
    <dubbo:annotation package="com.atguigu.controller" />

现在的扫描包

    <!--批量扫描, 范围要变大,不然扫描不到SpringSecurityUserService包-->
    <dubbo:annotation package="com.atguigu" />

注意:

此处原来扫描的包为com.atguigu.controller,现在改为com.atguigu包的目的是需要将我们上面定义的SpringSecurityUserService也扫描到,因为在SpringSecurityUserService的loadUserByUsername方法中需要通过dubbo远程调用名称为UserService的服务

1.2.4.第四步:spring-security.xml

【路径】
1:定义哪些链接可以放行
2:定义哪些链接不可以方向,即需要有角色、权限才可以放行
3:认证管理,定义登录账号和密码,并授权访问的角色、权限
4:设置在页面可以通过iframe访问受保护的页面,默认为不允许默认访问,需要添加security:frame-optionspolicy=”SAMEORIGIN“
【讲解】
在meinian_web工程中提供spring-security.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd
                  http://www.springframework.org/schema/mvc
                  http://www.springframework.org/schema/mvc/spring-mvc.xsd
                  http://code.alibabatech.com/schema/dubbo
                  http://code.alibabatech.com/schema/dubbo/dubbo.xsd
                  http://www.springframework.org/schema/context
                  http://www.springframework.org/schema/context/spring-context.xsd
                          http://www.springframework.org/schema/security
                          http://www.springframework.org/schema/security/spring-security.xsd">

    <!--静态资源放行 对css不做权限判断-->
    <security:http security="none" pattern="/css/**"/>
    <security:http security="none" pattern="/img/**"/>
    <security:http security="none" pattern="/js/**"/>
<!--    <security:http security="none" pattern="/pages/**"/>-->
    <security:http security="none" pattern="/template/**"/>
    <security:http security="none" pattern="/plugins/**"/>
    <security:http security="none" pattern="/login.html"/>

    <!-- 对控制器中的方法提供注解的权限验证-->
    <security:global-method-security pre-post-annotations="enabled"/>

    <security:http auto-config="true" use-expressions="true">
        <security:intercept-url pattern="/pages/**" access="isAuthenticated()"/>

        <security:form-login login-page="/login.html"
                             login-processing-url="/login.do"
                             username-parameter="username"
                             password-parameter="password"
                             default-target-url="/pages/main.html"
                             always-use-default-target="true"
                             authentication-failure-forward-url="/login.html"
        />
        <!--禁用csrf的获取使用-->
        <security:csrf disabled="true"/>

        <security:logout logout-success-url="/login.html" logout-url="//www.greatytc.com/logout.do" invalidate-session="true"/>

        <security:headers>
            <!--设置在页面可以通过iframe访问受保护的页面,默认为不允许访问-->
            <security:frame-options policy="SAMEORIGIN"/>
        </security:headers>

        <security:access-denied-handler ref="customAccessDeniedHandler"/>
    </security:http>

    <!--认证authentication(登录)-->
    <security:authentication-manager>
        <!--使用userServiceImpl进行登录-->
        <security:authentication-provider user-service-ref="userServiceImpl">
            <security:password-encoder ref="bcry"/>
        </security:authentication-provider>
    </security:authentication-manager>

    <bean id="bcry" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

</beans>

在spring-security.xml中添加
放置到 <security:http auto-config="true" use-expressions="true"> 里面

        <security:headers>
            <!--设置在页面可以通过iframe访问受保护的页面,默认为不允许访问-->
            <security:frame-options policy="SAMEORIGIN"/>
        </security:headers>

因为我们在main.html中定义: 如果不配置springSecurity 会认为iframe访问的html页面时受保护的页面,不允许访问。

1.2.5.第五步: springmvc.xml

在springmvc,xml文件中引入spring-security.xml文件

    <import resource="spring-security.xml"/>

1.2.6.第六步: TravelItemController 类 授权

在Controller的方法上加入权限 控制注解,此处以TravelItemController为例

    @RequestMapping("/update")
    @PreAuthorize("hasAuthority('TRAVELITEM_EDIT')") // 权限校验
    public Result update(@RequestBody TravelItem travelItem){

        try {
            travelItemService.update(travelItem);
            return new Result(true, MessageConstant.EDIT_TRAVELITEM_SUCCESS);

        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false, MessageConstant.EDIT_TRAVELITEM_FAIL);

        }
    }

1.2.7第七步: CustomAccessDeniedHandler类

添加页面,没有权限时提示信息设置
1.在<security:http>标签中增加<security:access-denied-handler>

        <!--    自定义异常处理类    -->
        <security:access-denied-handler ref="customAccessDeniedHandler"/>

2.增加自定义处理类


    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
                       AccessDeniedException accessDeniedException) throws IOException, ServletException {
        if (isAjaxRequest(request)) {// AJAX请求,使用response发送403
            Result result = new Result(false, "无权访问","403");
            String json = JSON.toJSONString(result);
            response.getWriter().print(json);// 不要使用prinln方法,含有回车换行符
        } else{// 同步请求处理
            request.getRequestDispatcher("/pages/error/403.html").forward(request,response);
        }
    }


    /**
     * 判断是否为ajax请求
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        if (request.getHeader("accept").indexOf("application/json") > -1
                || (request.getHeader("X-Requested-With") != null
                && request.getHeader("X-Requested-With").equalsIgnoreCase("XMLHttpRequest"))) {
            return true;
        }
        return false;
    }

3.增加/pages/error/403.html页面

1.2.8.第八步: 导入login.html页面

1.3.显示用户名

【路径】
1:引入js
2:定义username属性
3:使用钩子函数,调用ajax,查询登录用户(从SpringSecurity中获取),复制username属性
4:修改页面,使用{username}显示用户信息
【讲解】
前面我们已经完成了认证和授权操作,如果用户认证成功后需要在页面显示当前用户的用户名,Spring Security在认证成功后会将用户信息保存到框架提供的上下文对象中,所以此处我们就可以调用Spring Security框架提供的api获取当前用户的username 并展示到页面上
实现步骤:
第一步:在mian.html页面中修改,定义username模型数据基于VUE的数据展示用户名,发送Ajax请求获取username
(1):引入js

<scipt src ="../js/axios-0.18.0.js">

(2):定义username属性
(3):使用钩子函数,调用ajax
(4):修改页面
显示当前登录人

1.4用户退出

【路径】
1:在main,html中提供的退出菜单上加入超链接
2:在Spring-security.xml文件中配置
【讲解】
第一步:在main.html中提供的退出菜单上加入超链接

                                <el-dropdown-item divided>
                                        <span style="display:block;"><a href="//www.greatytc.com/logout.do">退出</a></span>
                                    </el-dropdown-item>

第二步:在Spring-security.xml文件中配置

        <security:logout logout-url="//www.greatytc.com/logout.do" logout-success-url="/login.html" invalidate-session="true"/>

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

推荐阅读更多精彩内容