spring security使用hibernate进行查询数据库验证
Posted 飞天0407
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring security使用hibernate进行查询数据库验证相关的知识,希望对你有一定的参考价值。
前面查询数据库采用的都是jdbc方式,如果系统使用的是hibernate,该如何进行呢,下面就是实现步骤,关键还是实现自定义的UserDetailsService
项目结构如下:
使用hibernate,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.petter</groupId> <artifactId>security-hibernate-annotation</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>security-hibernate-annotation Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring.version>4.3.5.RELEASE</spring.version> <spring.security.version>4.2.1.RELEASE</spring.security.version> <mysql.connector.version>5.1.40</mysql.connector.version> <dbcp.version>2.1.1</dbcp.version> <hibernate.version>5.2.9.Final</hibernate.version> </properties> <dependencies> <!-- database pool --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>${dbcp.version}</version> </dependency> <!-- Hibernate ORM --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- ORM integration, e.g Hibernate --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring + aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring4</artifactId> <version>3.0.3.RELEASE</version> </dependency> <!-- Spring Security --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${spring.security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${spring.security.version}</version> </dependency> <!-- 用于thymeleaf中使用security的标签 --> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> <version>3.0.2.RELEASE</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.connector.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>utf8</encoding> </configuration> </plugin> </plugins> </build> </project>
一、由于使用hibernate,我们使用jpa自己生成数据库表,具体是
1、用于实现基于持久化Token的记住我功能的表persistent_logins,对应的类是PersistentLogin
package com.petter.model; import javax.persistence.*; import java.util.Date; /** * @author hongxf * @since 2017-04-17 14:42 */ @Entity @Table(name = "persistent_logins") public class PersistentLogin { private String series; private String username; private String token; private Date lastUsed; @Id @Column(name = "series") public String getSeries() { return series; } public void setSeries(String series) { this.series = series; } @Column(name = "username", nullable = false) public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Column(name = "token", nullable = false) public String getToken() { return token; } public void setToken(String token) { this.token = token; } @Temporal(TemporalType.TIMESTAMP) @Column(name = "last_used", nullable = false) public Date getLastUsed() { return lastUsed; } public void setLastUsed(Date lastUsed) { this.lastUsed = lastUsed; } }
2、用户类User对应表users
package com.petter.model; import javax.persistence.*; import java.util.HashSet; import java.util.Set; /** * @author hongxf * @since 2017-04-17 10:29 */ @Entity @Table(name = "users") public class User { private String username; private String password; private boolean enabled; private boolean accountNonExpired; private boolean accountNonLocked; private boolean credentialsNonExpired; private Set<UserRole> userRole = new HashSet<>(); @Id @Column(name = "username", unique = true, nullable = false, length = 45) public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Column(name = "password", nullable = false, length = 60) public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Column(name = "enable", nullable = false) public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } @Column(name = "accountNonExpired", nullable = false) public boolean isAccountNonExpired() { return accountNonExpired; } public void setAccountNonExpired(boolean accountNonExpired) { this.accountNonExpired = accountNonExpired; } @Column(name = "accountNonLocked", nullable = false) public boolean isAccountNonLocked() { return accountNonLocked; } public void setAccountNonLocked(boolean accountNonLocked) { this.accountNonLocked = accountNonLocked; } @Column(name = "credentialsNonExpired", nullable = false) public boolean isCredentialsNonExpired() { return credentialsNonExpired; } public void setCredentialsNonExpired(boolean credentialsNonExpired) { this.credentialsNonExpired = credentialsNonExpired; } @OneToMany(fetch = FetchType.EAGER, mappedBy = "user") public Set<UserRole> getUserRole() { return userRole; } public void setUserRole(Set<UserRole> userRole) { this.userRole = userRole; } }
3、用户权限表user_roles对应类UserRole
package com.petter.model; import javax.persistence.*; /** * @author hongxf * @since 2017-04-17 10:32 */ @Entity @Table(name = "user_roles", uniqueConstraints = @UniqueConstraint(columnNames = {"role", "username"})) public class UserRole { private Integer userRoleId; private User user; private String role; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "user_role_id") public Integer getUserRoleId() { return userRoleId; } public void setUserRoleId(Integer userRoleId) { this.userRoleId = userRoleId; } @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "username", nullable = false) public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Column(name = "role", nullable = false, length = 45) public String getRole() { return role; } public void setRole(String role) { this.role = role; } }
4、用于保存登录失败尝试次数的UserAttempts
package com.petter.model; import javax.persistence.*; import java.util.Date; /** * @author hongxf * @since 2017-03-20 10:50 */ @Entity @Table(name = "user_attempts") public class UserAttempts { private int id; private String username; private int attempts; private Date lastModified; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") public int getId() { return id; } public void setId(int id) { this.id = id; } @Column(name = "username") public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Column(name = "attempts") public int getAttempts() { return attempts; } public void setAttempts(int attempts) { this.attempts = attempts; } @Temporal(TemporalType.TIMESTAMP) @Column(name = "lastModified") public Date getLastModified() { return lastModified; } public void setLastModified(Date lastModified) { this.lastModified = lastModified; } }
二、配置hibernate,在AppConfig类中添加hibernate的配置
package com.petter.config; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.SessionFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.orm.hibernate5.HibernateTransactionManager; import org.springframework.orm.hibernate5.LocalSessionFactoryBuilder; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect; import org.thymeleaf.spring4.SpringTemplateEngine; import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver; import org.thymeleaf.spring4.view.ThymeleafViewResolver; import org.thymeleaf.templatemode.TemplateMode; import java.util.Properties; /** * 相当于 * @author hongxf * @since 2017-03-08 10:11 */ @EnableWebMvc @Configuration @ComponentScan({"com.petter.*"}) @EnableTransactionManagement @Import({SecurityConfig.class}) public class AppConfig { @Bean public SessionFactory sessionFactory() { LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource()); builder.scanPackages("com.petter.model") .addProperties(getHibernateProperties()); return builder.buildSessionFactory(); } private Properties getHibernateProperties() { Properties prop = new Properties(); prop.put("hibernate.format_sql", "true"); prop.put("hibernate.show_sql", "true"); prop.put("hibernate.hbm2ddl.auto", "update"); prop.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect"); return prop; } @Bean(name = "dataSource") public BasicDataSource dataSource() { BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://192.168.11.81:3306/security_learning_3"); ds.setUsername("petter"); ds.setPassword("petter"); return ds; } @Bean public HibernateTransactionManager txManager() { return new HibernateTransactionManager(sessionFactory()); } @Bean public SpringResourceTemplateResolver springResourceTemplateResolver() { SpringResourceTemplateResolver springResourceTemplateResolver = new SpringResourceTemplateResolver(); springResourceTemplateResolver.setPrefix("/WEB-INF/pages/"); springResourceTemplateResolver.setSuffix(".html"); springResourceTemplateResolver.setTemplateMode(TemplateMode.HTML); springResourceTemplateResolver.setCacheable(false); springResourceTemplateResolver.setCharacterEncoding("UTF-8"); return springResourceTemplateResolver; } @Bean public SpringTemplateEngine springTemplateEngine() { SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine(); springTemplateEngine.setTemplateResolver(springResourceTemplateResolver()); springTemplateEngine.addDialect(new SpringSecurityDialect()); return springTemplateEngine; } @Bean public ThymeleafViewResolver thymeleafViewResolver() { ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver(); thymeleafViewResolver.setTemplateEngine(springTemplateEngine()); thymeleafViewResolver.setCharacterEncoding("UTF-8"); return thymeleafViewResolver; } }
注意这里添加了事务注解@EnableTransactionManagement,否则运行会报无事务错误,并且需要在具体的repository中添加@Transactional注解
三、使用hibernate查询数据库获得User
1、定义接口UserDao
package com.petter.dao; import com.petter.model.User; /** * @author hongxf * @since 2017-04-17 10:41 */ public interface UserDao { User findByUserName(String username); }
2、实现接口UserDao
package com.petter.dao.impl; import com.petter.dao.UserDao; import com.petter.model.User; import org.hibernate.SessionFactory; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; /** * @author hongxf * @since 2017-04-17 10:42 */ @Repository public class UserDaoImpl implements UserDao { @Resource private SessionFactory sessionFactory; @SuppressWarnings("unchecked") @Transactional(readOnly = true) @Override public User findByUserName(String username) { List<User> users = sessionFactory.getCurrentSession() .createQuery("from User where username = ?") .setParameter(0, username) .list(); if (users.size() > 0) { return users.get(0); } else { return null; } } }
四、实现自定义UserDetailsService
package com.petter.service; import com.petter.dao.UserDao; import com.petter.model.User; import com.petter.model.UserRole; 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 javax.annotation.Resource; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * security的验证过程会调用指定的UserDetailsService * 自定义的UserDetailsService查询数据库得到自己User后 * 组装org.springframework.security.core.userdetails.User返回 * @author hongxf * @since 2017-04-17 10:45 */ @Service("userDetailsService") public class CustomUserDetailsService implements UserDetailsService { @Resource private UserDao userDao; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userDao.findByUserName(username); if以上是关于spring security使用hibernate进行查询数据库验证的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Spring Security @Secured 注解测试 Spring Boot @WebFluxTest
Spring Framework,Spring Security - 可以在没有 Spring Framework 的情况下使用 Spring Security?
仅使用 Spring-Security(或)Spring 进行授权
在使用 Oauth、SAML 和 spring-security 的多租户的情况下从 spring-security.xml 中获取错误