java笔记----springMvc简单整合spring-security示例

900 阅读2分钟

maven引入依赖

当前是用tomcat插件的形式来启动服务器

<!--当前项目打包形式-->
<packaging>war</packaging>

<!--统一管理当前依赖版本 指定编译环境-->
<properties>
    <spring.version>5.0.5.RELEASE</spring.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>

<!--引入相关依赖【坐标】-->
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>5.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>5.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

<!--tomcat服务器 以插件的形式引入-->
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <configuration>
                <!-- 指定端口 -->
                <port>85</port>
                <!-- 请求路径 -->
                <path>/</path>
            </configuration>
        </plugin>
    </plugins>
</build>

当前项目是war包 所以创建webapp 在web.xml文件中配置 springMvc前端控制器 和springSecurityFilterChain 过滤器链

注意:当前 springSecurityFilterChain 过滤器链 是每个认证授权必须首先调用的 且是自动调用【当前是如何自动注册的 我也不会 请大神指教】所以名称 必须是springSecurityFilterChain 否则会报错 找不到bean对象

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">

    <filter>
        <!--
         1: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>

    <!-- 2:springmvc的核心控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-security.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        
        <!-- 注意 当前.do 的目的是为了请求路径的时候 只要后缀是 .do的就会运行前端控制器 如果没有.do就是运行原生servlet解析 这样做的好处就是不用不用在spring配置文件中配置静态源映射-->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

spring配置文件中配置spring-security认证授权的服务类 密码加密方式

<!--认证:
默认:BCryptPasswordEncoder
修改:NoOpPasswordEncoder  采用明文解析密码
-->
<security:authentication-manager>
<!--指定当前认证授权的服务类-->
    <security:authentication-provider user-service-ref="userDetailsServiceImpl">
        <!--指定密码加密策略-->
        <security:password-encoder ref="passwordEncoder"></security:password-encoder>

        <!--
        <security:user-service>
            <security:user name="admin" authorities="ROLE_ADMIN" password="{noop}admin"></security:user>
        </security:user-service>
        -->
    </security:authentication-provider>
</security:authentication-manager>

<!--配置密码加密对象-->
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />

自定义认证授权的服务类

当前并没有连接数据库 使用静态代码块初始化的数据 包括用户信息 用户权限

一般的最简单的权限认证授权应该是5张表 用户表 角色表 权限表 用户角色表 用户权限表【这应该是最简单的配置啦吧 如有错:请大神指正】
package com.wang.service;

import com.wang.entity.Auth;
import com.wang.entity.User;
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.*;
import java.util.function.Consumer;

@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    //模拟从数据库 获取用户信息和权限
    static HashMap<String,User> map = new HashMap<>();


    //初始化当前用户数据
    static {
        Auth auth = new Auth("用户添加", "USER_ADD");
        Auth auth1 = new Auth("用户删除", "USER_DELETE");
        Auth auth3 = new Auth("用户修改", "USER_EDIT");
        Auth auth2 = new Auth("用户插叙", "USER_FIND");
        
         //这里用Set 虽然我初始化的数据没有角色 但是正常情况下 一个角色对应多个权限 所以权限不免有重复的
        Set<Auth> authSet1 = new HashSet<>();
        authSet1.add(auth);
        authSet1.add(auth1);
        Set<Auth> authSet2 = new HashSet<>();
        authSet2.add(auth2);
        authSet2.add(auth3);
        
        //这里想要密码不是加密后的 可以设置明文解析 否则密码解析错误这些权限就无法验证了 当前密码是123
        User user1 = new User("1", "admin", "$2a$10$F0AZYwSJeQqbfoALUGF66uZMQPZBKl93QlLPx0t2w7Yp9xhujrL7K", authSet1);
        User user2 = new User("2", "wang", "$2a$10$F0AZYwSJeQqbfoALUGF66uZMQPZBKl93QlLPx0t2w7Yp9xhujrL7K", authSet2);

        map.put("admin",user1);
        map.put("wang",user2);
    }


    /**
     * 获取用户信息 包括权限列表
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //根据账号查询用户信息 和 权限
        User user = map.get(username);

        if(user == null){
            return null;
        }
        //
        // 设置当前用户的信息  当前用户拥有的权限列表
        String password = user.getPassword();

        List<GrantedAuthority> list = new ArrayList<>();
        
        //这里也可以用普通的for循环 如果lambda表达式不明白的话
        user.getAuthList().forEach(auth -> list.add(new SimpleGrantedAuthority(auth.getAuthText())));

        //为什么这样返回数据 上面为什么这样保存权限 建议直接官网或百度 
        return new org.springframework.security.core.userdetails.User(username,password,list);
    }
}

spring-security.xml配置文件中指定一些资源的访问 需要某个角色或权限

<!--指定当前路径下的资源不需要认证或授权 如果不单独配置那些路径需要认证或授权 也是不受权限认证影响的可以随便访问-->
<security:http security="none" pattern="/js/**" />
<security:http security="none" pattern="/css/**" />
<security:http security="none" pattern="/img/**" />
<security:http security="none" pattern="/login.html" />

<security:http auto-config="true" use-expressions="true">
    <!--<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"></security:intercept-url>-->

    <!--只要认证通过就可以访问-->
    <security:intercept-url pattern="/index.html"  access="isAuthenticated()" />
    <security:intercept-url pattern="/a.html"  access="isAuthenticated()" />

    <!--拥有add权限就可以访问b.html页面-->
    <security:intercept-url pattern="/b.html"  access="hasAuthority('add')" />

    <!--拥有ROLE_ADMIN角色就可以访问c.html页面,
        注意:此处虽然写的是ADMIN角色,框架会自动加上前缀ROLE_-->
    <security:intercept-url pattern="/c.html"  access="hasRole('ROLE_ADMIN')" />

    <!--拥有ROLE_ADMIN角色就可以访问d.html页面-->
    <security:intercept-url pattern="/d.html"  access="hasRole('ABC')" />

    <!--授权自定义登录页-->
    <security:form-login login-page="/login.html"
                         username-parameter="username"
                         password-parameter="password"
                         login-processing-url="/login.do"
                         default-target-url="/index.html"
                         authentication-failure-url="/login.html"
                         always-use-default-target="true"/>
    <!--授权注销功能-->
    <security:logout logout-url="/logout.do"
                     logout-success-url="/login.html"
                     invalidate-session="true"/>

    <!--禁用CSRF  也就是当前请求可以跨域访问  默认不可以跨域-->
    <security:csrf disabled="true"></security:csrf>
</security:http>

controller层如何用注解的方式限制当前请求 需要指定的角色或授权

第一步 spring-security.xml配置文件中 开启注解权限控制

<!--开启注解方式权限控制-->
<security:global-method-security pre-post-annotations="enabled" />

第二步 controller包下新建类 HelloController

package com.atguigu.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


//通过@PreAuthorize注解对方法访问授权。可以在controller,也可以在service方法。我觉得在controller层做校验 层次比较清晰
@RestController
@RequestMapping("/hello")
public class HelloController {

    @RequestMapping("/add")
    @PreAuthorize("hasAuthority('add')")//表示用户必须拥有add权限才能调用当前方法
    public String add(){
        System.out.println("add...");
        return "success";
    }

    @RequestMapping("/update")
    @PreAuthorize("hasRole('ROLE_ADMIN')")//表示用户必须拥有ROLE_ADMIN角色才能调用当前方法
    public String update(){
        System.out.println("update...");
        return "success";
    }

    @RequestMapping("/delete")
    @PreAuthorize("hasRole('ABC')")//表示用户必须拥有ABC角色才能调用当前方法
    public String delete(){
        System.out.println("delete...");
        return "success";
    }
}
最后这个简单示例就完成了 运行的时候 请从maven中找插件tomcat运行

image.png