Spring Security笔记:使用数据库进行用户认证(form login using database)

在前一节,学习了如何自定义登录页,但是用户名、密码仍然是配置在xml中的,这样显然太非主流,本节将学习如何把用户名/密码/角色存储在db中,通过db来实现用户认证

一、项目结构

与前面的示例相比,因为要连接db,所以多出了一个spring-database.xml用来定义数据库连接,此外,为了演示登录用户权限不足的场景,加了一个页面403.jsp,用来统一显示权限不足的提示信息

二、数据库表结构(oracle环境)

 1 create table T_USERS
 2 (
 3   d_username VARCHAR2(50) not null,
 4   d_password VARCHAR2(60),
 5   d_enabled  NUMBER(1)
 6 );
 7 alter table T_USERS
 8   add constraint PK_USERS_USERNAME primary key (D_USERNAME) ;
 9   
10 create table T_USER_ROLES
11 (
12   d_user_role_id NUMBER(10) not null,
13   d_username     VARCHAR2(50),
14   d_role         VARCHAR2(50)
15 );
16 alter table T_USER_ROLES
17   add constraint PK_USER_ROLES primary key (D_USER_ROLE_ID);
18 alter table T_USER_ROLES
19   add constraint IDX_UNI_ROLE_USERNAME unique (D_USERNAME, D_ROLE);

这里创建了二张表,一张用来保存用户名/密码,另一张用来保存用户所属的权限角色,表名和字段名无所谓,可以随便改,但是用户表中,必须要有"用户名/密码/帐号的有效状态"这三列信息,权限角色表必须要有“用户名/权限角色”这二列信息

再insert几条测试数据

 1 insert into T_USERS (D_USERNAME, D_PASSWORD, D_ENABLED)
 2 values ('YJMYZZ', '123456', 1);
 3 
 4 insert into T_USERS (D_USERNAME, D_PASSWORD, D_ENABLED)
 5 values ('MIKE', 'MIKE123', 1); 
 6 
 7 insert into T_USER_ROLES (D_USER_ROLE_ID, D_USERNAME, D_ROLE)
 8 values (1, 'MIKE', 'POWER');
 9 
10 insert into T_USER_ROLES (D_USER_ROLE_ID, D_USERNAME, D_ROLE)
11 values (2, 'YJMYZZ', 'ADMIN');
12 
13 insert into T_USER_ROLES (D_USER_ROLE_ID, D_USERNAME, D_ROLE)
14 values (3, 'YJMYZZ', 'POWER');

这里插入了二个用户YJMYZZ/MIKE,而且MIKE属于POWER组,YJMYZZ同时属于POWER\ADMIN二个权限组

三、spring-security.xml

 1 <beans:beans xmlns="http://www.springframework.org/schema/security"
 2     xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xsi:schemaLocation="http://www.springframework.org/schema/beans
 4     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 5     http://www.springframework.org/schema/security
 6     http://www.springframework.org/schema/security/spring-security-3.2.xsd">
 7 
 8     <http auto-config="true" use-expressions="true">
 9         <intercept-url pattern="/admin**" access="hasRole('ADMIN')" />
10         <!-- access denied page -->
11         <access-denied-handler error-page="/403" />
12         <form-login login-page="/login" default-target-url="/welcome"
13             authentication-failure-url="/login?error" username-parameter="username"
14             password-parameter="password" />
15         <logout logout-success-url="/login?logout" />
16         <!-- enable csrf protection -->
17         <csrf />
18     </http>
19 
20     <!-- Select users and user_roles from database -->
21     <authentication-manager>
22         <authentication-provider>
23             <jdbc-user-service data-source-ref="dataSource"
24                 users-by-username-query="select d_username username,d_password password, d_enabled enabled from t_users where d_username=?"
25                 authorities-by-username-query="select d_username username, d_role role from t_user_roles where d_username=?  " />
26         </authentication-provider>
27     </authentication-manager>
28 
29 </beans:beans>

注意第9行,这里使用了一个el表达式,目的是/admin开头的url,必须有ADMIN角色的登录用户才可访问

第11行,表示如果登录用户权限不够,将跳转到/403这个url

24,25这二行,指定了查询用户/角色的sql语句,注意:虽然前面提到了用户/角色这二张表的表名/字段名可以随便写,但是写sql时,用户名的别名必须是username,密码列的别名必须是password,帐号有效状态的别名必须是enabled,而权限角色列的别名必须是role

23行指定了db数据源,它的详细定义在 spring-database.xml中,内容如下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 5     http://www.springframework.org/schema/beans/spring-beans.xsd">
 6 
 7     <bean id="dataSource"
 8         class="org.springframework.jdbc.datasource.DriverManagerDataSource">
 9         <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
10         <property name="url" value="jdbc:oracle:thin:@172.21.***.***:1521:orcl" />
11         <property name="username" value="***" />
12         <property name="password" value="***" />
13     </bean>
14 </beans>

本文使用的是oracle数据库,如果是其它数据库,请自行调整上面的内容

四、Controller 

 1 package com.cnblogs.yjmyzz;
 2 
 3 import org.springframework.security.authentication.AnonymousAuthenticationToken;
 4 import org.springframework.security.core.Authentication;
 5 import org.springframework.security.core.context.SecurityContextHolder;
 6 import org.springframework.security.core.userdetails.UserDetails;
 7 import org.springframework.stereotype.Controller;
 8 import org.springframework.web.bind.annotation.RequestMapping;
 9 import org.springframework.web.bind.annotation.RequestMethod;
10 import org.springframework.web.bind.annotation.RequestParam;
11 import org.springframework.web.servlet.ModelAndView;
12 
13 @Controller
14 public class HelloController {
15 
16     @RequestMapping(value = { "/", "/welcome" }, method = RequestMethod.GET)
17     public ModelAndView welcome() {
18 
19         ModelAndView model = new ModelAndView();
20         model.addObject("title",
21                 "Spring Security Login Form - Database Authentication");
22         model.addObject("message", "This is default page!");
23         model.setViewName("hello");
24         return model;
25 
26     }
27 
28     @RequestMapping(value = "/admin", method = RequestMethod.GET)
29     public ModelAndView admin() {
30 
31         ModelAndView model = new ModelAndView();
32         model.addObject("title",
33                 "Spring Security Login Form - Database Authentication");
34         model.addObject("message", "This page is for ROLE_ADMIN only!");
35         model.setViewName("admin");
36         return model;
37 
38     }
39 
40     @RequestMapping(value = "/login", method = RequestMethod.GET)
41     public ModelAndView login(
42             @RequestParam(value = "error", required = false) String error,
43             @RequestParam(value = "logout", required = false) String logout) {
44 
45         ModelAndView model = new ModelAndView();
46         if (error != null) {
47             model.addObject("error", "Invalid username and password!");
48         }
49 
50         if (logout != null) {
51             model.addObject("msg", "You've been logged out successfully.");
52         }
53         model.setViewName("login");
54 
55         return model;
56 
57     }
58 
59     // for 403 access denied page
60     @RequestMapping(value = "/403", method = RequestMethod.GET)
61     public ModelAndView accesssDenied() {
62 
63         ModelAndView model = new ModelAndView();
64 
65         // check if user is login
66         Authentication auth = SecurityContextHolder.getContext()
67                 .getAuthentication();
68         if (!(auth instanceof AnonymousAuthenticationToken)) {
69             UserDetails userDetail = (UserDetails) auth.getPrincipal();
70             model.addObject("username", userDetail.getUsername());
71         }
72 
73         model.setViewName("comm/403");
74         return model;
75 
76     }
77 
78 }

66-71行演示了如何在服务端判断一个用户是否已经登录

五、视图页面

hello.jsp

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <%@taglib prefix="sec"
 4     uri="http://www.springframework.org/security/tags"%>
 5 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
 6 <html>
 7 <head>
 8 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 9 <title>${title}</title>
10 </head>
11 <body>
12     <h1>Title : ${title}</h1>
13     <h1>Message : ${message}</h1>
14     <sec:authorize access="hasRole('POWER')">
15         <!-- For login user -->
16         <c:url value="/j_spring_security_logout" var="logoutUrl" />
17         <form action="${logoutUrl}" method="post" id="logoutForm">
18             <input type="hidden" name="${_csrf.parameterName}"
19                 value="${_csrf.token}" />
20         </form>
21         <script>
22             function formSubmit() {
23                 document.getElementById("logoutForm").submit();
24             }
25         </script>
26 
27         <c:if test="${pageContext.request.userPrincipal.name != null}">
28             <h2>
29                 User : ${pageContext.request.userPrincipal.name} | <a
30                     href="javascript:formSubmit()"> Logout</a> | <a href="admin">admin</a>
31             </h2>
32         </c:if>
33     </sec:authorize>
34 
35     <sec:authorize access="isAnonymous()">
36         <br />
37         <h2>
38             <a href="login">login</a>
39         </h2>
40     </sec:authorize>
41 
42 </body>
43 </html>

注意一下:14、27、35这三行,它们演示了如何在jsp端判断用户具有的角色权限、是否已登录等用法

403.jsp

 1 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
 2 <html>
 3 <body>
 4     <h1>HTTP Status 403 - Access is denied</h1>
 5 
 6     <c:choose>
 7         <c:when test="${empty username}">
 8             <h2>You do not have permission to access this page!</h2>
 9         </c:when>
10         <c:otherwise>
11             <h2>
12                 Username : ${username} <br /> You do not have permission to access
13                 this page!
14             </h2>
15         </c:otherwise>
16     </c:choose>
17 
18 </body>
19 </html>

admin.jsp

 1 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
 2 <%@page session="true"%>
 3 <html>
 4 <body>
 5     <h1>Title : ${title}</h1>
 6     <h1>Message : ${message}</h1>
 7 
 8     <c:url value="/j_spring_security_logout" var="logoutUrl" />
 9     <form action="${logoutUrl}" method="post" id="logoutForm">
10         <input type="hidden" name="${_csrf.parameterName}"
11             value="${_csrf.token}" />
12     </form>
13     <script>
14         function formSubmit() {
15             document.getElementById("logoutForm").submit();
16         }
17     </script>
18 
19     <c:if test="${pageContext.request.userPrincipal.name != null}">
20         <h2>
21             Welcome : ${pageContext.request.userPrincipal.name} | <a
22                 href="javascript:formSubmit()"> Logout</a> | <a href="welcome">welcome</a>
23         </h2>
24     </c:if>
25 
26 </body>
27 </html>

因为在xml中已经配置了/admin开头的请求url,必须具有ADMIN角色权限,所以admin.jsp端反而不用任何额外的判断了

文中示例源代码下载:SpringSecurity-LoginForm-Database-XML.zip

参考文章: Spring Security Form Login Using Database

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Laoqi's Linux运维专列

LAMP安装mysql 时遇到的问题汇总

1: 缺少 libaio 包, libaio是Linux下的一个异步非阻塞方式读写文件的接口。 1 2 3 [[email protect...

3866
来自专栏余林丰

Spring——Web应用中的IoC容器创建(WebApplicationContext根应用上下文的创建过程)

基于Spring-4.3.7.RELEASE Spring的配置不仅仅局限在XML文件,同样也可以使用Java代码来配置。在这里我使用XML配置文件的方式来粗略...

2525
来自专栏乐沙弥的世界

mydumper安装及安装故障汇总

   mydumper是针对mysql数据库备份的一个轻量级第三方的开源工具,备份方式术语逻辑备份。它支持多线程,备份速度远高于原生态的mysqldump以及众...

1430
来自专栏好好学java的技术栈

SpringMVC+RestFul详细示例实战教程一(实现跨域访问+postman测试)

注意:由于文章篇幅太长,超出了字数,这是文章的第一部分,明天分享文章的第二部分,请见谅!

6002
来自专栏程序猿DD

Spring Security 入门(五):在 Spring-Boot中的应用

? 前言 本文作为入门级的DEMO,完全按照官网实例演示; 项目目录结构 ? Maven 依赖 <parent> <groupId>org.sprin...

43115
来自专栏菩提树下的杨过

spring-boot 速成(8) 集成druid+mybatis

spring-boot与druid、mybatis集成(包括pageHelper分页插件), 要添加以下几个依赖项: compile('mysql:my...

9279
来自专栏闵开慧

hadoop错误代码查询

经常遇到的exception是:PipeMapRed.waitOutputThreads(): subprocess failed with code N "O...

4546
来自专栏码匠的流水账

聊聊spring security oauth2的几个endpoint的认证

本文就来讲一下spring security oauth2的几个endpoint的认证

5702
来自专栏扎心了老铁

springboot kafka集成(实现producer和consumer)

本文介绍如何在springboot项目中集成kafka收发message。 1、先解决依赖 springboot相关的依赖我们就不提了,和kafka相关的只依赖...

9065
来自专栏Albert陈凯

2018-11-17 面试必问问题TransactionalJava事务之一——Java事务的基本问题

关于加@Transactional注解的方法之间调用,事务是否生效的问题 https://blog.csdn.net/blacktal/article/det...

2702

扫码关注云+社区

领取腾讯云代金券