Spring有很多不同的模块。 所有这些对于具体目的都是有用的。 今天,我将讨论Spring Security。 该模块提供了灵活的方法来管理访问Web应用程序不同部分的许可。 在这篇文章中,我将研究Spring MVC , Hibernate , MySQL与Spring Security的集成。
任何Web应用程序的常规情况都是某些用户组之间的功能分离。 例如,具有“主持人”角色的用户可以编辑数据库中的现有记录。 一个用户
具有“管理员”角色的用户可以执行与具有“主持人”角色的用户相同的操作,并创建新记录。 在Spring MVC中,可以使用Spring Security来实现许可管理。
目标
作为示例,我将在Hibernate中使用示例Spring MVC应用程序。 用户及其角色将存储在数据库中。 MySQL将用作数据库。 我将创建三个表:用户,角色,user_roles。 您可能会猜到user_roles表是一个中间表 。 在应用程序中将扮演两个角色:主持人和管理员。 将有几个页面可供主持人和管理员访问。
制备
为了使Spring Security在项目中可用,只需在pom.xml文件中添加以下依赖项:
<!-- Spring Security --><dependency><groupid>org.springframework.security</groupid><artifactid>spring-security-core</artifactid><version>3.1.3.RELEASE</version></dependency><dependency><groupid>org.springframework.security</groupid><artifactid>spring-security-web</artifactid><version>3.1.3.RELEASE</version></dependency><dependency><groupid>org.springframework.security</groupid><artifactid>spring-security-config</artifactid><version>3.1.3.RELEASE</version></dependency>
我必须在数据库中创建三个表,并在其中插入几条记录。
CREATE TABLE `roles` (`id` int(6) NOT NULL AUTO_INCREMENT,`role` varchar(20) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;CREATE TABLE `users` (`id` int(6) NOT NULL AUTO_INCREMENT,`login` varchar(20) NOT NULL,`password` varchar(20) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;CREATE TABLE `user_roles` (`user_id` int(6) NOT NULL,`role_id` int(6) NOT NULL,KEY `user` (`user_id`),KEY `role` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
这是角色和用户的代码:
INSERT INTO hibnatedb.roles (role) VALUES ('admin'), ('moderator');INSERT INTO hibnatedb.users (login, password) VALUES ('moder', '111111'), ('adm', '222222');INSERT INTO hibnatedb.user_roles (user_id, role_id) VALUES (1, 2), (2, 1);
主要部分
项目的完整结构具有以下结构:
由于您可以在GitHub上找到该项目,因此我将忽略当前主题之外的一些内容。 我想从每个Web项目的心脏开始,我的意思是web.xml文件。 Spring Security基于简单的过滤器,因此我需要在部署描述符中添加过滤器的声明:
...<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>
...
现在是时候为用户和角色表创建实体了:
@Entity
@Table(name="users")
public class User {@Id@GeneratedValueprivate Integer id;private String login;private String password;@OneToOne(cascade=CascadeType.ALL)@JoinTable(name="user_roles",joinColumns = {@JoinColumn(name="user_id", referencedColumnName="id")},inverseJoinColumns = {@JoinColumn(name="role_id", referencedColumnName="id")})private Role role;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getLogin() {return login;}public void setLogin(String login) {this.login = login;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Role getRole() {return role;}public void setRole(Role role) {this.role = role;} }
和
@Entity
@Table(name="roles")
public class Role {@Id@GeneratedValueprivate Integer id;private String role;@OneToMany(cascade=CascadeType.ALL)@JoinTable(name="user_roles", joinColumns = {@JoinColumn(name="role_id", referencedColumnName="id")},inverseJoinColumns = {@JoinColumn(name="user_id", referencedColumnName="id")})private Set userRoles;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getRole() {return role;}public void setRole(String role) {this.role = role;}public Set getUserRoles() {return userRoles;}public void setUserRoles(Set userRoles) {this.userRoles = userRoles;}}
每个实体类都需要DAO和Service层。
public interface UserDAO {public User getUser(String login);}
和
@Repository
public class UserDAOImpl implements UserDAO {@Autowiredprivate SessionFactory sessionFactory;private Session openSession() {return sessionFactory.getCurrentSession();}public User getUser(String login) {List userList = new ArrayList();Query query = openSession().createQuery("from User u where u.login = :login");query.setParameter("login", login);userList = query.list();if (userList.size() > 0)return userList.get(0);elsereturn null; }}
分别用于Role类:
public interface RoleDAO {public Role getRole(int id);}
和
@Repository
public class RoleDAOImpl implements RoleDAO {@Autowiredprivate SessionFactory sessionFactory;private Session getCurrentSession() {return sessionFactory.getCurrentSession();}public Role getRole(int id) {Role role = (Role) getCurrentSession().load(Role.class, id);return role;}}
服务层使用相同的对:
public interface UserService {public User getUser(String login);}
和
@Service
@Transactional
public class UserServiceImpl implements UserService {@Autowiredprivate UserDAO userDAO;public User getUser(String login) {return userDAO.getUser(login);}}
分别用于Role类:
public interface RoleService {public Role getRole(int id);}
和
@Service
@Transactional
public class RoleServiceImpl implements RoleService {@Autowiredprivate RoleDAO roleDAO;public Role getRole(int id) {return roleDAO.getRole(id);}}
以上只是机械的常规代码。 现在让我们研究Spring Security代码。 为了将Spring Security插入到项目中,我必须创建CustomUserDetailsService类并实现UserDetailsService接口。
import java.util.ArrayList;
import java.util.Collection;
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.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 com.sprsec.dao.UserDAO;@Service
@Transactional(readOnly=true)
public class CustomUserDetailsService implements UserDetailsService {@Autowiredprivate UserDAO userDAO; public UserDetails loadUserByUsername(String login)throws UsernameNotFoundException {com.sprsec.model.User domainUser = userDAO.getUser(login);boolean enabled = true;boolean accountNonExpired = true;boolean credentialsNonExpired = true;boolean accountNonLocked = true;return new User(domainUser.getLogin(), domainUser.getPassword(), enabled, accountNonExpired, credentialsNonExpired, accountNonLocked,getAuthorities(domainUser.getRole().getId()));}public Collection getAuthorities(Integer role) {List authList = getGrantedAuthorities(getRoles(role));return authList;}public List getRoles(Integer role) {List roles = new ArrayList();if (role.intValue() == 1) {roles.add("ROLE_MODERATOR");roles.add("ROLE_ADMIN");} else if (role.intValue() == 2) {roles.add("ROLE_MODERATOR");}return roles;}public static List getGrantedAuthorities(List roles) {List authorities = new ArrayList();for (String role : roles) {authorities.add(new SimpleGrantedAuthority(role));}return authorities;}}
该类的主要目的是将应用程序的User类映射到Spring Security的User类。 这是Spring Security的杀手级功能之一。 这样,您可以使任何种类的Spring MVC应用程序适应Security模块的使用。
控制器和视图
有关Spring Security的最常见问题之一是如何创建自定义登录表单 。 答案很简单。 您需要使用该表单创建一个JSP文件,并在其中指定action属性()。
URL映射的大部分取决于spring-security.xml文件:
...<http auto-config="true"><intercept-url pattern="/sec/moderation.html" access="ROLE_MODERATOR"><intercept-url pattern="/admin/*" access="ROLE_ADMIN"><form-login login-page="/user-login.html" default-target-url="/success-login.html" authentication-failure-url="/error-login.html"><logout logout-success-url="/index.html"></logout></form-login></intercept-url></intercept-url></http><authentication-manager><authentication-provider user-service-ref="customUserDetailsService"><password-encoder hash="plaintext"></password-encoder></authentication-provider></authentication-manager>
...
如您所见,我为以下各项指定了URL:登录页面,成功登录后的默认页面,凭据无效情况下的错误页面。 我也声明了需要一些访问许可的URL。 最重要的是身份验证管理器的声明。 通过这种方式,Spring Security将使用数据库来识别用户及其角色。
控制器:
@Controller
public class LinkNavigation {@RequestMapping(value="/", method=RequestMethod.GET)public ModelAndView homePage() {return new ModelAndView("home");}@RequestMapping(value="/index", method=RequestMethod.GET)public ModelAndView indexPage() {return new ModelAndView("home");}@RequestMapping(value="/sec/moderation", method=RequestMethod.GET)public ModelAndView moderatorPage() {return new ModelAndView("moderation");}@RequestMapping(value="/admin/first", method=RequestMethod.GET)public ModelAndView firstAdminPage() {return new ModelAndView("admin-first");}@RequestMapping(value="/admin/second", method=RequestMethod.GET)public ModelAndView secondAdminPage() {return new ModelAndView("admin-second");}}
和
@Controller
public class SecurityNavigation {@RequestMapping(value="/user-login", method=RequestMethod.GET)public ModelAndView loginForm() {return new ModelAndView("login-form");}@RequestMapping(value="/error-login", method=RequestMethod.GET)public ModelAndView invalidLogin() {ModelAndView modelAndView = new ModelAndView("login-form");modelAndView.addObject("error", true);return modelAndView;}@RequestMapping(value="/success-login", method=RequestMethod.GET)public ModelAndView successLogin() {return new ModelAndView("success-login");}}
您可以在GitHub上看到的视图 。
请注意在WebAppConfig java类中添加@ImportResource(“ classpath:spring-security.xml”)。
摘要
我认为本文将帮助您深入了解Spring Security。 我在这里使用了Hibernate和MySQL,因为这样的技术组合在Internet上的其他教程中并不经常使用。 可能您注意到我在项目中使用了一些XML,这是因为当前尚无办法使用基于注释的方法来实现所有这些东西。
翻译自: https://www.javacodegeeks.com/2013/05/spring-mvc-security-with-mysql-and-hibernate.html