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

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_ROLES11 (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_ROLES17   add constraint PK_USER_ROLES primary key (D_USER_ROLE_ID);18 alter table T_USER_ROLES19   add constraint IDX_UNI_ROLE_USERNAME unique (D_USERNAME, D_ROLE);
create-table

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

再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);
insert user/role data

这里插入了二个用户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>
spring-security

注意第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 @Controller14 public class HelloController {15 16     @RequestMapping(value = http://www.mamicode.com/{"/", "/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 = "http://www.mamicode.com/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 = "http://www.mamicode.com/login", method = RequestMethod.GET)41     public ModelAndView login(42             @RequestParam(value = "http://www.mamicode.com/error", required = false) String error,43             @RequestParam(value = "http://www.mamicode.com/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 page60     @RequestMapping(value = "http://www.mamicode.com/403", method = RequestMethod.GET)61     public ModelAndView accesssDenied() {62 63         ModelAndView model = new ModelAndView();64 65         // check if user is login66         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 }
HelloController

五、视图页面

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} | <a30                     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>
hello.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 access13                 this page!14             </h2>15         </c:otherwise>16     </c:choose>17 18 </body>19 </html>
403.jsp

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} | <a22                 href="javascript:formSubmit()"> Logout</a> | <a href="welcome">welcome</a>23         </h2>24     </c:if>25 26 </body>27 </html>
admin.jsp

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

参考文章: Spring Security Form Login Using Database