• 【JavaEE】SSH+Spring Security整合及example


    前文为止,SSH的基本框架都已经搭建出来了,现在,在这基础上再加上权限控制,也就是Spring Security框架,和前文的顺序一样,先看看需要加哪些库。

    1. pom.xml

    Spring Security只需要加上自己的库就可以,先定义一个版本的属性:

    <properties>
        <spring.version>4.0.4.RELEASE</spring.version>
        <hibernate.version>4.3.5.Final</hibernate.version>
        <spring-security.version>3.2.4.RELEASE</spring-security.version>
    </properties>

    然后加入spring-security的包:

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

    当然还有spring-security-core和spring-security-web等必须的包,不过都会被作为依赖导入。

    2. web.xml

    Spring Security是作为过滤器控制权限的,所以,要在web.xml中配置这个过滤器:

    <filter>
        <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>

    其他的东西,就是被Spring框架来管理了,Spring Security配置的地方和hibernate的配置一样,可以加在applicationContext.xml中,但是hibernate要单独弄一个infrastructure.xml,Security也单独写一个配置文件,叫做applicationContext-security.xml,因为这个的名字和前面的非常像,可以合并在一起来配置contextConfigLocation:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:/META-INF/applicationContext*.xml,
                    classpath:/META-INF/infrastructure.xml</param-value>
    </context-param>

    classpath后面的*代表有多个文件,后面的*是一个通配符,所以凡是/META-INF/下所有applicationContext开头的xml文件都会被读取。

    3. User.java/Role.java

    这里先不说applicationContext-security.xml,先来看看需要哪些数据表,首先创建一个权限类Role(org.zhangfc.demo4ssh.domain.Role):

    @Entity
    @Table
    public class Role implements Serializable { private static final long serialVersionUID = -7425304725239042741L; private int id; private String role; @Id public int getId() { return id; } public void setId(int id) { this.id = id; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } }

    权限表就两个字段,id和一个权限名,接着修改User表:

    @Entity
    @Table
    public class User implements Serializable { private static final long serialVersionUID = 172643386440351811L; private int id; private String username; private String password; private Role role; @Id @GeneratedValue public int getId() { return id; } @Size(min=6) public String getUsername() { return username; }public String getPassword() { return password; } @ManyToOne public Role getRole() { return role; } // setter method of id / username / password / role
    }

    为了节省篇幅,我删掉了所有的setter方法,这儿增加了两项,一个id,还有一个多对一的外键指向Role表。把properties/hibernate.properties中的hibernate.hbm2dll.auto属性设置为update,并把HomeController的home方法插入新用户的代码注释掉或删掉,首先把这个程序运行起来,让程序创建role表并更新user表,然后编辑数据表(我用的navicat),在role表中加入下面的两条记录:

    之后修改user表,之前的数据不会被清空,而是新添加了两个空字段:

    这样就有了两个可登陆的账号,下面就来写一下配置文件。

    4. applicationContext-security.xml

    先贴下所有的配置再做解释

    <beans:beans xmlns="http://www.springframework.org/schema/security"
        xmlns:beans="http://www.springframework.org/schema/beans" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                            http://www.springframework.org/schema/security
                            http://www.springframework.org/schema/security/spring-security-3.2.xsd">
     
        <http auto-config="true">
            <intercept-url pattern="/json**" access="ROLE_ADMIN,ROLE_USER" />
            <intercept-url pattern="/admin**" access="ROLE_ADMIN" />
            <form-login 
                login-page="/" 
                default-target-url="/"
                authentication-failure-url="/?login=error" />
                
            <logout logout-success-url="/" />
        </http>
     
        <authentication-manager>
            <authentication-provider>
                <!-- <password-encoder hash="md5">
                    <salt-source user-property="email"/>
                </password-encoder> -->
                <jdbc-user-service data-source-ref="dataSource"
                    users-by-username-query="select username, password, 1 from user where username = ?" 
                    authorities-by-username-query="select u.username, r.role from user u left join role r on u.role_id=r.id where username = ?" 
                />
            </authentication-provider>
        </authentication-manager>
        
    </beans:beans>

    先看这个authentication-manager,这是一个认证管理器,用户名密码的认证就是它来干的,Spring security有一套默认的规则,个人认为也没太大必要去改这个默认规则,那就是对根目录下/j_spring_security_check的网络请求,会被作为登陆请求,并获取j_username和j_password作为参数进行用户名密码的匹配,这时候spring会把j_username交给一个provider,这个provider的任务就是根据这个用户名返回一个包含用户名、密码、权限(权限可以是数组)的对象。在这个地方,我用了spring security自己的authentication-provider,引用前面在配置hibernate的配置的数据源,并分别执行两条sql语句去根据用户名查询密码、当前用户是否enable及该用户权限。

    登录成功之后,所有的网络请求,都会根据当前用户的role去最前面的intercept-url上去匹配(json和admin后面的两个**,第一个代表通配路径,第二个代表通配子目录),如果当前用户的权限是ROLE_USER,但是对应url的access域中没有ROLE_USER或者IS_AUTHENTICATED_ANONYMOUSLY(表示允许匿名访问),那么就不能访问,这时候就会跳转到下面设置的login-page去让用户登录,当然,登录成功之前所有的请求也会经过这个链,所以千万不要把登陆界面设置什么访问权限,要不就死循环了,登录时如果登录成功了会跳转到default-target-url设置的地址,如果登录失败了,则会跳到authentication-failure-url上去,现在我是设置的无论怎么样都跳回根目录。

    如果是注销,也有一个默认的规则,就是访问根目录下的j_spring_security_logout就可以了,注销之后页面会跳转到logout-success-url。

    5. index.jsp

    我们访问根目录会访问HomeController的home方法,之后进入index.jsp上,所以这里就编辑pages/index.jsp(pages是WEB-INF的同级目录),加上登录用的表单:

    <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
    
    <c:set var="base" value="${pageContext.request.contextPath }/" scope="session"/>
    <sec:authentication property="principal" var="auth" scope="session" />
    
    <html>
    <body>
    <h2>Hello World!</h2>
    <h2>${auth }</h2>
    <form action="${base }j_spring_security_check" method="post">
        用户名:<input type="text" name="j_username" /><br/>
        密码:<input type="password" name="j_password" /><br/>
        <input type="submit" value="登录" />
    </form>
    <a href="${base }j_spring_security_logout">注销</a>
    </body>
    </html>

    运行程序,访问项目根目录:

    我在index.jsp中使用security的标签获得了一个叫做principal的对象,这个对象在未登录的时候是一个字符串,我把它打印了出来,可以看到值是"anonymousUser"。登录的时候是一个用户对象(不是我们自己定义的那个domain.User,而是spring的一个UserDetails对象),这个一会登录之后再看。

    用zhangsan/123456登录,点登录之后看到,原来打印anonymousUser的地方变成了一个对象的描述:

    org.springframework.security.core.userdetails.User@aa9c3074: Username: zhangsan; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN

    这时候如果把index.jsp里的${auth}改成${auth.username}就可以打印出zhangsan。接下来访问:http://localhost:8080/demo4ssh-security/json和http://localhost:8080/demo4ssh-security/admin都是可以正确访问的,因为zhangsan是个ROLE_ADMIN,这两个目录都有访问权限,但是,根据前面的配置ROLE_USER是不能访问admin的,下面再次进入根目录,点注销之后用wangwu/234567登录(注销之后如果是打印${auth.username}就会抛异常,因为auth这个对象没有username这个域了),登录之后发现http://localhost:8080/demo4ssh-security/json依旧可以正常访问,http://localhost:8080/demo4ssh-security/admin却不能访问了:

    Spring Security的基本配置就是这样了,相比较前面的几个而言,Spring Security这么基础的配置基本没什么大用,现在谁还能用明文存密码,用加盐的方式MD5加密是好配置的(我写的是用email作为盐,但是出于简单考虑,我这个demo的user表里没有没有email,所以需要用MD5的时候注意一下),applicationContext-security.xml中有一段被我注释掉的配置就是干这个的,但是,更多的密码存储要比这个还要复杂,而且,没有访问权限也不能返回这么个界面,还是那句话,我现在只想用最简单的方式搭出一个能用的框架,至于这些细节,只要基本框架能跑了就是小意思了,这些问题还是留待以后再写。

    源码下载

  • 相关阅读:
    随手
    会使用基本的Render函数后,就会想,这怎么用 v-for/v-if/v-model;我写个vue Render函数进阶
    iframe子页面与父页面元素的访问以及js变量的访问[zhuan]
    vue element-ui 的奇怪组件el-switch
    URLSearchParams和axios的post请求(防忘记)
    ios vue2.0使用html5中的audio标签不能播放音乐
    Unity Input System教程
    关于OpenGPU.org
    Better ultra_simple for Slamtec RPLIDAR on Linux
    八字心得
  • 原文地址:https://www.cnblogs.com/smarterplanet/p/4087577.html
Copyright © 2020-2023  润新知