在现在的应用当中,我们都会把用户信息都存到数据库当中,所以我们要把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 把授权信息写入数据库
用STS(Spring Tool Suite)开发的,spring mvc + spring security 实现的最简单的登录系统,无数据库。
Spring security认证授权例子,自动创建数据库,在SysUser类增加字段,即可动态增加数据库对应表sys_user字段(前提是要删除原表,启动应用时才会重建表)
主题: Spring Security —— 演讲者 张明星 1、Traditional Web App Security Dev 2、Spring Security 2.x Quick Start 3、Spring Security 2.x Overview 4、Dive Into Spring Security Authentication ...
1.本项目为SpringCloud Gateway的微服务框架,整合了SpringSecurity,微服务间使用Redis来获取登陆的用户信息。 2.由于Gat
spring-security使用数据库用户认证
基于springboot+SpringSecurity的一款权限认证系统源码+数据库.zip基于springboot+SpringSecurity的一款权限认证系统源码+数据库.zip基于springboot+SpringSecurity的一款权限认证系统源码+数据库.zip基于springboot...
springboot+ spring security实现登录认证
Spring Security三份资料,实战Spring Security 3.x.pdf;Spring Security 3.pdf;Spring Security使用手册.pdf
项目中使用到的技术包含SpringBoot、SpringSecurity&oauth2(安全资源和授权中心模式、包括登录接口自定义返回字段、自定义手机号+密码登录、自定义免密登录)、Queue队列、线程池、xss攻击配置、SpringCache、Mybatis...
springsecurity是一个功能强大且高度可定制的身份验证和访问控制框架。springsecurity是一个...最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。
springboot springsecurity动态权限控制,实现数据库动态管理菜单权限
SpringSecurity框架的权限认证流程原理,请求到来时SpringSecurity如果调用层层过滤器来完成认证;
SpringBoot+SpringSecurity+Vue后台管理系统的开发源代码(前端+后端+数据库)SpringBoot+SpringSecurity+Vue后台管理系统的开发源代码(前端+后端+数据库)SpringBoot+SpringSecurity+Vue后台管理系统的开发源代码...
理解Spring Security的工作原理,Spring Security结构总览,认证流程和授权,中间涉及到哪些组件,这些组件分 别处理什么,如何自定义这些组件满足个性需求。 OAuth2.0认证的四种模式?它们的大体流程是什么? ...
Spring Security:spring家族一员。是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转...
spring security spring security 中文文档
spring security 使用及配置
Spring boot+Spring Security Oauth2.0,Sprint cloud+Spring Security Oauth2集成。四种认证方式。附带有代码,和案例,案例,还有视频链接。我保证看完就回,如果视频链接失效,评论回复我,我单独再给你一份。
视频详细讲解,需要的小伙伴自行网盘下载,链接见附件,永久有效。 首先,SSM环境中我们通过xml配置的...Springsecurity在两种不同的开发模式中使用,有经典的独立web后台管理系统,也有时下最流行的前后端分离场景。