<authentication-manager > <authentication-provider> <user-service> <user name="jack" passWord="jack123" authorities="ROLE_USER" /> <user name="admin" password="admin123" authorities="ROLE_ADMIN" /> <user name="dba" password="dba123" authorities="ROLE_ADMIN,ROLE_DBA" /> </user-service> </authentication-provider> </authentication-manager>This will be changed to following to support database authentication:<authentication-manager > <authentication-provider user-service-ref="myUserDetailsService" /> </authentication-manager>All the credentials are now stored in database, and will be accessible to Spring Security via org.springframework.security.core.userdetails.UserDetailsService implementations. We will provide an implementation of UserDetailsService which will eventually use our fully transactional user defined userService to access data from database. That's it. Rest of the setup for this post is usual Spring Security, Spring MVC and trivial Mybatis Setup which we have seen many times in previous tutorials.Below is the full example code for this post. We have divided the responsibilities into separate layers(service/dao) to make it manageable.2. pom.xml:<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.npf</groupId> <artifactId>sping-security-role-based-login</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>sping-security-role-based-login Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <spring.version>4.1.6.RELEASE</spring.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>MySQL</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.30</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>4.0.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>4.0.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>4.0.1.RELEASE</version> </dependency> </dependencies> <profiles> <profile> <id>jdk-1.7</id> <activation> <activeByDefault>true</activeByDefault> <jdk>1.7</jdk> </activation> <properties> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> <maven.compiler.compilerVersion>1.7</maven.compiler.compilerVersion> </properties> </profile> </profiles> <build> <finalName>sping-security-role-based-login</finalName> </build></project>3. spring-security.xml:<?xml version="1.0" encoding="UTF-8"?><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-4.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd"> <beans:bean id="securityContextLogoutHandle" class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/> <http auto-config="true" use-expressions="true"> <intercept-url pattern="/" access="hasRole('ADMIN') or hasRole('USER') or hasRole('DBA')" /> <intercept-url pattern="/home" access="hasRole('ADMIN') or hasRole('USER') or hasRole('DBA')" /> <intercept-url pattern="/admin/**" access="hasRole('ADMIN')" /> <intercept-url pattern="/dba/**" access="hasRole('DBA')" /> <access-denied-handler error-page="/accessDenied" /> <form-login login-page="/login" username-parameter="ssoId" password-parameter="password" login-processing-url="/login" authentication-failure-url="/authenticationFailure"/> </http> <authentication-manager > <authentication-provider user-service-ref="myUserDetailsService" /> </authentication-manager> </beans:beans>4. 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>5. Define UserDetailsService implementationThis service is responsible for providing authentication details to Authentication Manager. It implements Spring’s UserDetailsService interface, which contains only one method loadUserByUsername, taking username[ssoId in our example] and returns a org.springframework.security.core.userdetails.User object. We will populate this object using our own UserService which gets data from db using UserDao object.package com.npf.service.impl;import java.util.ArrayList;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;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.Service;import com.npf.entity.Role;import com.npf.entity.User;import com.npf.service.UserService;import com.npf.util.State;@Service("myUserDetailsService")public class MyUserDetailsServiceImpl implements UserDetailsService { @Autowired private UserService userService; @Override public UserDetails loadUserByUsername(String ssoId)throws UsernameNotFoundException { User user = userService.findBySsoId(ssoId); System.out.println("User : "+user); if(user==null){ System.out.println("User not found"); throw new UsernameNotFoundException("Username not found"); } boolean enabled = user.getState().equals(State.Active); List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); for(Role role : user.getRoleList()){ System.out.println("role : "+role); authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getRoleType())); } return new org.springframework.security.core.userdetails.User(user.getSsoId(), user.getPassword(), enabled, true, true, true, authorities); }}6. HelloWorldController:package com.npf.controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.Authentication;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;import org.springframework.stereotype.Controller;import org.springframework.ui.ModelMap;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;@Controllerpublic class HelloWorldController { @Autowired private SecurityContextLogoutHandler securityContextLogoutHandle; @RequestMapping(value = {"/home","/"}, method = RequestMethod.GET) public String homePage(ModelMap model) { Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); String userName = principal instanceof UserDetails ? ((UserDetails) principal) .getUsername() : principal.toString(); model.addAttribute("user", userName); return "welcome"; } @RequestMapping(value = "/admin/index", method = RequestMethod.GET) public String adminPage(ModelMap model) { Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); String userName = principal instanceof UserDetails ? ((UserDetails) principal) .getUsername() : principal.toString(); model.addAttribute("user", userName); return "admin/index"; } @RequestMapping(value = "/dba/index", method = RequestMethod.GET) public String dbaPage(ModelMap model) { Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); String userName = principal instanceof UserDetails ? ((UserDetails) principal) .getUsername() : principal.toString(); model.addAttribute("user", userName); return "dba/index"; } @RequestMapping(value = "/login", method = RequestMethod.GET) public String loginPage() { return "login"; } @RequestMapping(value = "/logout", method = RequestMethod.GET) public String logoutPage(HttpServletRequest request,HttpServletResponse response) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { securityContextLogoutHandle.logout(request, response, auth); } return "redirect:/home"; } @RequestMapping(value = "/accessDenied", method = RequestMethod.GET) public String accessDeniedPage(ModelMap model) { Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); String userName = principal instanceof UserDetails ? ((UserDetails) principal) .getUsername() : principal.toString(); model.addAttribute("user", userName); return "accessDenied"; } @RequestMapping(value = "/authenticationFailure", method = RequestMethod.GET) public String authenticationFailure(HttpServletRequest request){ request.setAttribute("authenticationFailureResult", "failure"); return "login"; }}7. User Model:package com.npf.entity;import java.io.Serializable;import java.util.List;import com.npf.util.State;public class User implements Serializable{ private static final long serialVersionUID = 1L; private int id; private String ssoId; private String password; private String firstName; private String lastName; private String email; private State state; private List<Role> roleList; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getSsoId() { return ssoId; } public void setSsoId(String ssoId) { this.ssoId = ssoId; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public State getState() { return state; } public void setState(State state) { this.state = state; } public List<Role> getRoleList() { return roleList; } public void setRoleList(List<Role> roleList) { this.roleList = roleList; }}8. Role Model:package com.npf.entity;import java.io.Serializable;import com.npf.util.RoleType;public class Role implements Serializable{ private static final long serialVersionUID = 1L; private int id; private RoleType roleType; public int getId() { return id; } public void setId(int id) { this.id = id; } public RoleType getRoleType() { return roleType; } public void setRoleType(RoleType roleType) { this.roleType = roleType; }}package com.npf.util;public enum RoleType { USER, DBA, ADMIN;}package com.npf.util;public enum State { Active, Inactive, Deleted, Locked;}9. Dao Layer:package com.npf.dao.base;import java.io.Serializable;public interface BaseDao<PK extends Serializable, T> { public T getEntityByKey(PK key); public T createEntity(T entity); public T updateEntity(T entity); public void deleteEntity(T entity); }package com.npf.dao.user;import com.npf.dao.base.BaseDao;import com.npf.entity.User;public interface UserDao extends BaseDao<Integer,User> { public User findBySsoId(String ssoId); }<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.npf.dao.user.UserDao"> <resultMap type="com.npf.entity.User" id="selectUserMap"> <id column="USER_ID" property="id"/> <result column="SSO_ID" property="ssoId"/> <result column="PASSWORD" property="password"/> <result column="FIRST_NAME" property="firstName"/> <result column="LAST_NAME" property="lastName"/> <result column="EMAIL" property="email"/> <result column="STATE" property="state"/> <collection property="roleList" ofType="com.npf.entity.Role"> <id column="ROLE_ID" property="id"/> <result column="ROLE_TYPE" property="roleType"/> </collection> </resultMap> <select id="findBySsoId" parameterType="string" resultMap="selectUserMap"> SELECT USER.ID as USER_ID,SSO_ID,PASSWORD,FIRST_NAME,LAST_NAME,EMAIL,STATE,ROLE.ID as ROLE_ID,ROLE_TYPE FROM USER INNER JOIN USER_ROLE_REL ON USER.ID = USER_ROLE_REL.USER_ID INNER JOIN ROLE ON ROLE.ID = USER_ROLE_REL.ROLE_ID WHERE SSO_ID = #{ssoId} </select> </mapper>10. Service Layer:package com.npf.service;import com.npf.entity.User;public interface UserService { public User findById(int id); public User findBySsoId(String ssoId);}package com.npf.service.impl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import com.npf.dao.user.UserDao;import com.npf.entity.User;import com.npf.service.UserService;@Service("userService")@Transactionalpublic class UserServiceImpl implements UserService{ @Autowired private UserDao userDao; @Override public User findById(int id) { return userDao.getEntityByKey(id); } @Override public User findBySsoId(String ssoId) { return userDao.findBySsoId(ssoId); }}11. Create Database Schema & Populate dummy data:/*Navicat MySQL Data TransferSource Server : localhostSource Server Version : 50624Source Host : localhost:3306Source Database : securityTarget Server Type : MYSQLTarget Server Version : 50624File Encoding : 65001Date: 2017-02-19 19:54:17*/SET FOREIGN_KEY_CHECKS=0;-- ------------------------------ Table structure for `role`-- ----------------------------DROP TABLE IF EXISTS `role`;CREATE TABLE `role` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `ROLE_TYPE` varchar(20) COLLATE utf8_bin NOT NULL, PRIMARY KEY (`ID`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;-- ------------------------------ Records of role-- ----------------------------INSERT INTO `role` VALUES ('1', 'USER');INSERT INTO `role` VALUES ('2', 'DBA');INSERT INTO `role` VALUES ('3', 'ADMIN');-- ------------------------------ Table structure for `user`-- ----------------------------DROP TABLE IF EXISTS `user`;CREATE TABLE `user` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `SSO_ID` varchar(20) COLLATE utf8_bin NOT NULL, `PASSWORD` varchar(20) COLLATE utf8_bin NOT NULL, `FIRST_NAME` varchar(20) COLLATE utf8_bin NOT NULL, `LAST_NAME` varchar(20) COLLATE utf8_bin NOT NULL, `EMAIL` varchar(20) COLLATE utf8_bin NOT NULL, `STATE` varchar(20) COLLATE utf8_bin NOT NULL, PRIMARY KEY (`ID`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;-- ------------------------------ Records of user-- ----------------------------INSERT INTO `user` VALUES ('1', 'jack', 'jack123', 'jack', 'nie', 'jack@QQ.com', 'Active');INSERT INTO `user` VALUES ('2', 'admin', 'admin123', 'admin', 'nie', 'admin@qq.com', 'Active');INSERT INTO `user` VALUES ('3', 'dba', 'dba123', 'dba', 'nie', 'dba@qq.com', 'Active');-- ------------------------------ Table structure for `user_role_rel`-- ----------------------------DROP TABLE IF EXISTS `user_role_rel`;CREATE TABLE `user_role_rel` ( `USER_ID` int(11) NOT NULL, `ROLE_ID` int(11) NOT NULL, PRIMARY KEY (`USER_ID`,`ROLE_ID`), KEY `roleIdFer` (`ROLE_ID`), CONSTRAINT `roleIdFer` FOREIGN KEY (`ROLE_ID`) REFERENCES `role` (`ID`), CONSTRAINT `userIdFer` FOREIGN KEY (`USER_ID`) REFERENCES `user` (`ID`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;-- ------------------------------ Records of user_role_rel-- ----------------------------INSERT INTO `user_role_rel` VALUES ('1', '1');INSERT INTO `user_role_rel` VALUES ('3', '2');INSERT INTO `user_role_rel` VALUES ('2', '3');INSERT INTO `user_role_rel` VALUES ('3', '3');二、测试
1. 访问主页: http://localhost:8080/sping-security-role-based-login/因为没有权限访问主页,所以被定向到了登录页面:2. 使用"ADMIN"权限登录页面(正确用户名和密码):
登录成功后,你将会看到:
3. 使用"ADMIN"权限登录页面(错误用户名和密码):
你将会看到:
项目的源代码地址:https://github.com/spring-security/spring-security-mybatis参考文献:1.Spring Security 4 Hibernate Integration Annotation+XML Example
新闻热点
疑难解答