Spring Security笔记:使用数据库进行用户认证(form login using database)
在前一节,学习了如何自定义登录页,但是用户名、密码仍然是配置在xml中的,这样显然太非主流,本节将学习如何把用户名/密码/角色存储在db中,通过db来实现用户认证
一、项目结构

与前面的示例相比,因为要连接db,所以多出了一个spring-database.xml用来定义数据库连接,此外,为了演示登录用户权限不足的场景,加了一个页面403.jsp,用来统一显示权限不足的提示信息
二、数据库表结构(oracle环境)
create table T_USERS
(
d_username VARCHAR2(50) not null,
d_password VARCHAR2(60),
d_enabled NUMBER(1)
);
alter table T_USERS
add constraint PK_USERS_USERNAME primary key (D_USERNAME) ; create table T_USER_ROLES
(
d_user_role_id NUMBER(10) not null,
d_username VARCHAR2(50),
d_role VARCHAR2(50)
);
alter table T_USER_ROLES
add constraint PK_USER_ROLES primary key (D_USER_ROLE_ID);
alter table T_USER_ROLES
add constraint IDX_UNI_ROLE_USERNAME unique (D_USERNAME, D_ROLE);
create-table
这里创建了二张表,一张用来保存用户名/密码,另一张用来保存用户所属的权限角色,表名和字段名无所谓,可以随便改,但是用户表中,必须要有"用户名/密码/帐号的有效状态"这三列信息,权限角色表必须要有“用户名/权限角色”这二列信息
再insert几条测试数据
insert into T_USERS (D_USERNAME, D_PASSWORD, D_ENABLED)
values ('YJMYZZ', '', 1); insert into T_USERS (D_USERNAME, D_PASSWORD, D_ENABLED)
values ('MIKE', 'MIKE123', 1); insert into T_USER_ROLES (D_USER_ROLE_ID, D_USERNAME, D_ROLE)
values (1, 'MIKE', 'POWER'); insert into T_USER_ROLES (D_USER_ROLE_ID, D_USERNAME, D_ROLE)
values (2, 'YJMYZZ', 'ADMIN'); insert into T_USER_ROLES (D_USER_ROLE_ID, D_USERNAME, D_ROLE)
values (3, 'YJMYZZ', 'POWER');
insert user/role data
这里插入了二个用户YJMYZZ/MIKE,而且MIKE属于POWER组,YJMYZZ同时属于POWER\ADMIN二个权限组
三、spring-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.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd"> <http auto-config="true" use-expressions="true">
<intercept-url pattern="/admin**" access="hasRole('ADMIN')" />
<!-- access denied page -->
<access-denied-handler error-page="/403" />
<form-login login-page="/login" default-target-url="/welcome"
authentication-failure-url="/login?error" username-parameter="username"
password-parameter="password" />
<logout logout-success-url="/login?logout" />
<!-- enable csrf protection -->
<csrf />
</http> <!-- Select users and user_roles from database -->
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select d_username username,d_password password, d_enabled enabled from t_users where d_username=?"
authorities-by-username-query="select d_username username, d_role role from t_user_roles where d_username=? " />
</authentication-provider>
</authentication-manager> </beans:beans>
spring-security
注意第9行,这里使用了一个el表达式,目的是/admin开头的url,必须有ADMIN角色的登录用户才可访问
第11行,表示如果登录用户权限不够,将跳转到/403这个url
24,25这二行,指定了查询用户/角色的sql语句,注意:虽然前面提到了用户/角色这二张表的表名/字段名可以随便写,但是写sql时,用户名的别名必须是username,密码列的别名必须是password,帐号有效状态的别名必须是enabled,而权限角色列的别名必须是role
23行指定了db数据源,它的详细定义在 spring-database.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@172.21.***.***:1521:orcl" />
<property name="username" value="***" />
<property name="password" value="***" />
</bean>
</beans>
本文使用的是oracle数据库,如果是其它数据库,请自行调整上面的内容
四、Controller
package com.cnblogs.yjmyzz; import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView; @Controller
public class HelloController { @RequestMapping(value = { "/", "/welcome" }, method = RequestMethod.GET)
public ModelAndView welcome() { ModelAndView model = new ModelAndView();
model.addObject("title",
"Spring Security Login Form - Database Authentication");
model.addObject("message", "This is default page!");
model.setViewName("hello");
return model; } @RequestMapping(value = "/admin", method = RequestMethod.GET)
public ModelAndView admin() { ModelAndView model = new ModelAndView();
model.addObject("title",
"Spring Security Login Form - Database Authentication");
model.addObject("message", "This page is for ROLE_ADMIN only!");
model.setViewName("admin");
return model; } @RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView login(
@RequestParam(value = "error", required = false) String error,
@RequestParam(value = "logout", required = false) String logout) { ModelAndView model = new ModelAndView();
if (error != null) {
model.addObject("error", "Invalid username and password!");
} if (logout != null) {
model.addObject("msg", "You've been logged out successfully.");
}
model.setViewName("login"); return model; } // for 403 access denied page
@RequestMapping(value = "/403", method = RequestMethod.GET)
public ModelAndView accesssDenied() { ModelAndView model = new ModelAndView(); // check if user is login
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
UserDetails userDetail = (UserDetails) auth.getPrincipal();
model.addObject("username", userDetail.getUsername());
} model.setViewName("comm/403");
return model; } }
HelloController
66-71行演示了如何在服务端判断一个用户是否已经登录
五、视图页面
hello.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="sec"
uri="http://www.springframework.org/security/tags"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${title}</title>
</head>
<body>
<h1>Title : ${title}</h1>
<h1>Message : ${message}</h1>
<sec:authorize access="hasRole('POWER')">
<!-- For login user -->
<c:url value="/j_spring_security_logout" var="logoutUrl" />
<form action="${logoutUrl}" method="post" id="logoutForm">
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}" />
</form>
<script>
function formSubmit() {
document.getElementById("logoutForm").submit();
}
</script> <c:if test="${pageContext.request.userPrincipal.name != null}">
<h2>
User : ${pageContext.request.userPrincipal.name} | <a
href="javascript:formSubmit()"> Logout</a> | <a href="admin">admin</a>
</h2>
</c:if>
</sec:authorize> <sec:authorize access="isAnonymous()">
<br />
<h2>
<a href="login">login</a>
</h2>
</sec:authorize> </body>
</html>
hello.jsp
注意一下:14、27、35这三行,它们演示了如何在jsp端判断用户具有的角色权限、是否已登录等用法
403.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>
<h1>HTTP Status 403 - Access is denied</h1> <c:choose>
<c:when test="${empty username}">
<h2>You do not have permission to access this page!</h2>
</c:when>
<c:otherwise>
<h2>
Username : ${username} <br /> You do not have permission to access
this page!
</h2>
</c:otherwise>
</c:choose> </body>
</html>
403.jsp
admin.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page session="true"%>
<html>
<body>
<h1>Title : ${title}</h1>
<h1>Message : ${message}</h1> <c:url value="/j_spring_security_logout" var="logoutUrl" />
<form action="${logoutUrl}" method="post" id="logoutForm">
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}" />
</form>
<script>
function formSubmit() {
document.getElementById("logoutForm").submit();
}
</script> <c:if test="${pageContext.request.userPrincipal.name != null}">
<h2>
Welcome : ${pageContext.request.userPrincipal.name} | <a
href="javascript:formSubmit()"> Logout</a> | <a href="welcome">welcome</a>
</h2>
</c:if> </body>
</html>
admin.jsp
因为在xml中已经配置了/admin开头的请求url,必须具有ADMIN角色权限,所以admin.jsp端反而不用任何额外的判断了
文中示例源代码下载:SpringSecurity-LoginForm-Database-XML.zip
参考文章: Spring Security Form Login Using Database
Spring Security笔记:使用数据库进行用户认证(form login using database)的更多相关文章
- Spring Security笔记:使用BCrypt算法加密存储登录密码
在前一节使用数据库进行用户认证(form login using database)里,我们学习了如何把“登录帐号.密码”存储在db中,但是密码都是明文存储的,显然不太讲究.这一节将学习如何使用spr ...
- spring security进阶 使用数据库中的账户和密码认证
目录 spring security 使用数据库中的账户和密码认证 一.原理分析 二.代码实现 1.新建一个javaWeb工程 2.用户认证的实现 3.测试 三.总结 spring security ...
- Spring Security笔记:HTTP Basic 认证
在第一节 Spring Security笔记:Hello World 的基础上,只要把Spring-Security.xml里改一个位置 <http auto-config="true ...
- spring security 动态 修改当前登录用户的 权限
1.前言 spring security 可以获取当前登录的用户信息,同时提供了接口 来修改权限列表信息 , 使用这个方法 ,可以动态的修改当前登录用户权限. 那么问题来了... 如果我是管理员 ,如 ...
- Spring Security 入门学习--数据库认证和授权
首先是使用的SpringBoot框架 基础需要的pom以来如下,基础的springboot项目的创建就不一一赘述了. <!--spring web--> <dependency> ...
- Spring Security笔记:Hello World
本文演示了Spring Security的最最基本用法,二个页面(或理解成二个url),一个需要登录认证后才能访问(比如:../admin/),一个可匿名访问(比如:../welcome) 注:以下内 ...
- 使用Spring Security Oauth2完成RESTful服务password认证的过程
摘要:Spring Security与Oauth2整合步骤中详细描述了使用过程,但它对于入门者有些重量级,比如将用户信息.ClientDetails.token存入数据库而非内存.配置 ...
- JavaEE学习之Spring Security3.x——模拟数据库实现用户,权限,资源的管理
一.引言 因项目需要最近研究了下Spring Security3.x,并模拟数据库实现用户,权限,资源的管理. 二.准备 1.了解一些Spring MVC相关知识: 2.了解一些AOP相关知识: 3. ...
- Spring Boot 2.0 利用 Spring Security 实现简单的OAuth2.0认证方式2
0.前言 经过前面一小节已经基本配置好了基于SpringBoot+SpringSecurity+OAuth2.0的环境.这一小节主要对一些写固定InMemory的User和Client进行扩展.实现动 ...
随机推荐
- TCP的粘包现象
看面经时,看到有面试官问TCP的粘包问题.想起来研一做购物车处理数据更新时遇到粘包问题,就总结一下吧. 1 什么是粘包现象 TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看, ...
- 解析ABP框架中的事务处理和工作单元,ABP事务处理
通用连接和事务管理方法连接和事务管理是使用数据库的应用程序最重要的概念之一.当你开启一个数据库连接,什么时候开始事务,如何释放连接...诸如此类的. 正如大家都知道的,.Net使用连接池(connec ...
- BIEE11G常用函数及使用说明
BIEE常用函数使用手册 1.AGGREGATE AT 此函数根据指定的级别聚合列.使用AGGREGATE AT 可确保始终在关键字AT 之后指定的级别执行度量聚合,而无论WHERE 子句如何. 语法 ...
- ORACLE绑定变量隐式转换导致性能问题
年后一次系统升级后,监控数据库的工具DPA发现数据库的Total Wait时间突然飙增,如下截图所示,数据库的总体等待时间对比升级前飙增了非常多 另外就是发现出现了较多的等待事件,主要有latch: ...
- mysql-4 数据检索(2)
用通配符进行过滤 like操作符 %通配符 %可以匹配任意字符 SELECT prod_id , prod_name FROM products WHERE prod_name LIKE 'je ...
- linux 中/proc 详解
proc 文件系统 在Linux中有额外的机制可以为内核和内核模块将信息发送给进程-- /proc 文件系统.最初设计的目的是允许更方便的对进程信息进行访问(因此得名),现在它被每一个有有趣的东西报告 ...
- 9、FTP封杀用户、限制传输速率、限制访问目录、为匿名用户提供下载资源
一.封杀某些用户访问FTP服务器 例如 封杀 yanji [root@localhost root]# vi /etc/vsftpd.ftpusers (用户控制配置文件,主要用于限 ...
- CSS样式----图文详解:css样式表和选择器
主要内容 CSS概述 CSS和HTML结合的三种方式:行内样式表.内嵌样式表.外部样式表 CSS四种基本选择器:标签选择器.类选择器.ID选择器.通用选择器 CSS三种扩展选择器:组合选择器.后代选择 ...
- openjudge6047分蛋糕[DP]
描述 有一块矩形大蛋糕,长和宽分别是整数w .h.现要将其切成m块小蛋糕,每个小蛋糕都必须是矩形.且长和宽均为整数.切蛋糕时,每次切一块蛋糕,将其分成两个矩形蛋糕.请计算:最后得到的m块小蛋糕中,最大 ...
- NOIP2000单词接龙[DFS]
题目描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合 ...