在现在的应用当中,我们都会把用户信息都存到数据库当中,所以我们要把security基于内存的操作改为基于数据库的操作。
其实不管基于内存的操作还是基于数据库的操作,security的目的都是只有一个,拿到当前的User以及相关的UserDetails信息,在基于内存的时候,用户名以及相关的UserDetails都会存到内存中,同理,如果迁移到数据库中,那么,我们可以从数据库中查出当前User以及相关的用户信息,然后封装成一个实现了UserDetails接口的User实现类。理解到这里,目标已经非常明确了:返回一个实现了UserDetails的User类给框架。
在security框架中,无论是InMemoryDaoImpl还是基于数据库的实现类都是利用同一个接口UserDetailService的方法loadUserByUsername。(这种面向接口的设计方式是非常棒的,纯属个人的感悟)。这里我们只需要改变这个方法的实现方式就可以达到目的了。
到这里,我们已经知道怎么样去把基于内存的操作迁移到数据库中了,下面的实现,只要按照这种思路来做,就不会有什么问题了。
首先,我们需要创建一个类实现UserDetailService接口,并且实现方法loadUserByUsername(下面是笔者自己的例子,仅供参考):
package service; import java.util.HashSet; import java.util.Set; import javax.annotation.Resource; import model.Users; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; 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 org.springframework.transaction.annotation.Transactional; import dao.BaseCollectionDao; @Service("hibernateUserDetailsService") public class HibernateUserDetailsService implements UserDetailsService { private BaseCollectionDao baseCollectionDao; @Resource(name="baseCollectionDao") public void setBaseCollectionDao(BaseCollectionDao baseCollectionDao) { this.baseCollectionDao = baseCollectionDao; } @Transactional public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Users users = baseCollectionDao.findUniqueByProperty(Users.class, "username", username); if(users == null) throw new UsernameNotFoundException("用户" + username + " 不存在!!!"); String[] roles = users.getRoles().split(","); Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>(); for(String role : roles) { authorities.add(new SimpleGrantedAuthority(role)); } return new User(users.getUsername(), users.getPassword(), authorities); } }
从上面的代码可以看到,User是从数据库查找出来的,而基于内存的操作,用户及其用户信息都是放到Map中的。为了方便对比,把InMemoryDaoImpl的实现方法也贴出来:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userMap.getUser(username); }
从上面两段代码的对比就可以看出,基于内存操作和基于数据库操作的根本区别。
接下来,定义好了UserDetailService,那么就应用到security框架中,在之前的篇章中已经说过了怎么配置,这里就简单贴出代码就行了:
<authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="hibernateUserDetailsService"> </authentication-provider> </authentication-manager>
因为dao层的操作每个人都不同,而这个问题并不重要,最主要的是把UserDetailService的loadUserByUsername的方法用基于数据库查询的方式实现。所以这里就不关注dao层的实现了。其实这里的变化就是把InMemoryDaoImpl类,换成了HibernateUserDetailService。
下面贴出本人的从service层到dao层的实现(security登录不需要controller层),以及配置文件(仅供参考):
hibernateUserDetailService:
package service; import java.util.HashSet; import java.util.Set; import javax.annotation.Resource; import model.Users; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; 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 org.springframework.transaction.annotation.Transactional; import dao.BaseCollectionDao; @Service("hibernateUserDetailsService") public class HibernateUserDetailsService implements UserDetailsService { private BaseCollectionDao baseCollectionDao; @Resource(name="baseCollectionDao") public void setBaseCollectionDao(BaseCollectionDao baseCollectionDao) { this.baseCollectionDao = baseCollectionDao; } @Transactional public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Users users = baseCollectionDao.findUniqueByProperty(Users.class, "username", username); if(users == null) throw new UsernameNotFoundException("用户" + username + " 不存在!!!"); String[] roles = users.getRoles().split(","); Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>(); for(String role : roles) { authorities.add(new SimpleGrantedAuthority(role)); } return new User(users.getUsername(), users.getPassword(), authorities); } }
dao层:
public <T> T findUniqueByProperty(Class<T> entityClass,final String propertyName,final Object value) { Session session = sessionFactory.getCurrentSession(); Criteria criteria = session.createCriteria(entityClass) .add( Restrictions.eq(propertyName, value) ); //增加属性相等约束 return (T) criteria.uniqueResult(); }
model层:
package model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="users") public class Users { private int id; private String username; private String password; private String roles; @Id @GeneratedValue(strategy=GenerationType.AUTO) public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getRoles() { return roles; } public void setRoles(String roles) { this.roles = roles; } }
security配置文件:
<?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-3.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"> <!-- 自上而下读取配置文件 --> <!-- 登录页面不过滤 --> <http pattern="/login.jsp*" security="none"/> <http pattern="/logoutsuccess.jsp" security="none" /> <http auto-config="true" use-expressions="true" > <form-login login-page="/login.jsp" always-use-default-target="true" default-target-url="/home.jsp"/> <intercept-url pattern="/**" access="hasRole('ROLE_USER')"/> <logout invalidate-session="true" logout-success-url="/logoutsuccess.jsp" logout-url="/logout"/> </http> <authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="hibernateUserDetailsService"> </authentication-provider> </authentication-manager> </beans:beans>
数据库配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> <!-- c3p0 dataSource --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 加载驱动 --> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <!-- database url --> <property name="jdbcUrl" value="jdbc:mysql://localhost/security" /> <!-- database username --> <property name="user" value="root" /> <!-- database password --> <property name="password" value="root" /> <!-- 连接关闭时默认将所有未提交的操作回滚。默认为false --> <property name="autoCommitOnClose" value="false" /> <!-- 连接池中保留的最小连接数 --> <property name="minPoolSize" value="5" /> <!-- 连接池中保留的最大连接数。默认为15 --> <property name="maxPoolSize" value="15" /> <!-- 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。默认为3 --> <property name="initialPoolSize" value="5" /> <!-- 最大空闲时间,超过空闲时间的连接将被丢弃。为0或负数则永不丢弃。默认为0秒 --> <property name="maxIdleTime" value="0" /> <!-- 当连接池中的连接用完时,C3P0一次性创建新连接的数目。默认为3 --> <property name="acquireIncrement" value="3" /> <!-- 定义在从数据库获取新连接失败后重复尝试获取的次数,默认为30 --> <property name="acquireRetryAttempts" value="30" /> <!-- 当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒,默认为0 --> <property name="checkoutTimeout" value="0" /> </bean> <!-- sessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.MySQL5Dialect <!-- cureentSesssion --> hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext <!-- Second-level caching hibernate.cache.region.factory_class=org.hibernate.cache.EhCacheRegionFactory net.sf.ehcache.configurationResourceName=/ehcache.xml hibernate.cache.use_second_level_cache=true hibernate.cache.use_query_cache=true --> <!-- sql --> hibernate.show_sql=true hibernate.format_sql=true hibernate.hbm2ddl.auto=update </value> </property> <property name="packagesToScan"> <value>model</value> </property> </bean> <!-- 事务管理 --> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- dao --> <context:component-scan base-package="dao.impl"></context:component-scan> </beans>
springMvc的配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd"> <mvc:annotation-driven/> <context:component-scan base-package="controller"/> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> </bean> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> </bean> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
上面就是本人的实现代码,由于使用的框架的不同,所以在配置方面会有所不同,仅供参考。
相关推荐
**使用数据库后台的Spring Security认证** - **配置**:定义数据源、用户细节服务以及密码编码器。 - **实现**:通过继承`UserDetailsService`接口并实现`loadUserByUsername`方法加载用户信息。 **基于JDBC的内置...
在本项目中,"springboot整合activity工作流审批前后台代码(有数据库)"是一个基于Spring Boot框架的应用,用于实现企业内部的工作流程审批系统。这个系统涵盖了员工、部门经理和BOSS三个不同的审批角色,旨在提供...
基于Spring Security实现用户认证与授权,确保只有合法用户才能进行拍卖操作。使用JWT(JSON Web Token)进行状态less的权限验证,提升系统的安全性。 六、API接口设计 前后端分离的架构中,后端提供RESTful API供...
8. **配置文件**:如数据库连接配置、安全配置(Spring Security)、定时任务配置(Spring Scheduler)等。 9. **静态资源**(CSS、JavaScript、图片等):用于前端页面的展示。 10. **测试类**:对各个模块进行...
后台的Spring Boot应用会将这些信息存储到数据库中,并能根据设定的规则进行会议冲突检测,确保不会安排重叠的会议。 2. **参会人员管理**:系统支持邀请并管理参会人员,包括添加、删除参会者,发送会议通知等。这...
### 基于Spring Boot的明星周边产品销售网站——《星之语》系统设计与实现 #### 一、项目背景及概述 随着信息技术的迅速发展,互联网已经渗透到生活的各个角落,无纸化作业成为现代企业的必然选择。《星之语》明星...
使用Spring Security或OAuth2等安全框架可以实现用户的认证和授权。用户信息应加密存储,保障用户隐私。 4. **商品与菜单管理**:商家可以添加、修改和删除菜品,创建和编辑菜单。后台数据库需设计菜品表,包含菜品...
1. 用户模块:包括用户注册、登录、个人信息管理等,使用Spring Security进行权限控制,确保用户信息的安全。 2. 商品模块:允许用户发布、编辑和删除二手商品,同时提供商品搜索和分类功能。 3. 购物车模块:用户...
本系统采用先进的技术栈——SSM(Spring、SpringMVC、MyBatis)+JSP,构建了一个高效、易用的共享客栈管理系统。下面将详细介绍这个系统的各个组成部分以及其背后的原理。 首先,SSM框架是Java开发Web应用的主流...
9. **Spring Security**:安全框架,确保系统的安全性,提供身份验证和授权等功能。 10. **Docker和Kubernetes**:系统支持Docker容器化部署,可以方便地在Kubernetes集群上进行管理和扩展。 Lilishop还提供了详细...
此外,考虑到安全性,可能还需要引入Spring Security或Apache Shiro等框架进行权限管理和认证。 总的来说,这个JSP课程设计项目不仅锻炼了开发者对JSP技术的掌握,还涵盖了数据库设计、Web应用架构、用户界面设计等...
《商城购物系统设计与实现——基于Java的SSM项目》 在信息技术日新月异的今天,商城购物系统的开发已经成为企业提升效率、拓展业务的重要手段。本项目是使用Java编程语言,结合Spring、SpringMVC和MyBatis(简称SSM...
同时,Spring Security或Apache Shiro等安全框架可以用来处理权限控制和认证问题,保护系统免受非法访问。 8. **测试** 为了保证系统的稳定性和可靠性,单元测试和集成测试是必不可少的。JUnit和Mockito等工具可以...
总结来说,"Java Web 项目开发案例精粹 第十四章 新闻发布系统"涵盖了Java Web开发的多个关键方面,包括后端编程、数据库设计、前端开发、框架应用、用户认证、安全管理以及测试策略。通过学习和实践这个案例,...
使用Spring Security或Apache Shiro进行权限配置。 三、技术选型 1. **后端开发**:Java后端开发可以选用Spring Boot框架,它简化了Spring应用的初始搭建以及开发过程。 2. **数据库连接**:使用JDBC或MyBatis...
在这种背景下,微信小程序凭借其独特的“用完即走”特性——无需下载安装即可使用的特性,迅速成为企业和开发者关注的焦点。它不仅简化了用户的使用流程,也为商家提供了高效便捷的服务渠道。 #### 二、研究内容与...
woodwhales-music项目作为一个SpringBoot应用,可能会使用一些常见的SpringBoot特性,如数据访问(JPA或MyBatis)、模板引擎(Thymeleaf或Freemarker)来渲染页面,以及安全控制(Spring Security)等。此外,项目...
Spring Data JPA用于简化数据库操作,而Spring Security则为系统提供了安全验证和授权机制。 2. **Vue.js前端框架** Vue.js是一个轻量级的前端MVVM框架,以其高可维护性和易用性受到广泛青睐。在本电商系统中,Vue...
这部分会涉及到Spring Security进行权限控制,使用Spring MVC处理请求,MyBatis进行数据操作。 2. **商品模块**:用户可以发布、修改、删除自己的二手商品,浏览他人发布的商品。这里需要实现商品分类、商品详情...
《驾校预约管理系统——Java技术深度解析》 驾校预约管理系统是一款基于Java技术开发的应用软件,旨在为驾校学员提供方便快捷的预约服务,同时也便于驾校管理员进行高效管理。在本篇文章中,我们将深入探讨该系统的...