登陆
Spring Security使用数据库认证
在Spring Security中如果想要使用数据进行认证操作,有很多种操作方式,这里我们介绍使用UserDetails、UserDetailsService来完成操作
UserDetails
UserDetails是一个接口,我们可以认为UserDetails作用是于封装当前进行认证的用户信息,但由于其是一个接口,所以我们可以对其进行实现,也可以使用Spring Security提供的一个UserDetails的实现类User来完成操作
以下是User类的部分代码
UserDetailsService
上面将UserDetails与UserDetailsService做了一个简单的介绍,那么我们具体如何完成Spring Security的数据库认证操作哪,我们通过用户管理中用户登录来完成Spring Security的认证操作。
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>数据 - AdminLTE2定制版 | Log in</title> <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport"> <link rel="stylesheet" href="${pageContext.request.contextPath}/plugins/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="${pageContext.request.contextPath}/plugins/font-awesome/css/font-awesome.min.css"> <link rel="stylesheet" href="${pageContext.request.contextPath}/plugins/ionicons/css/ionicons.min.css"> <link rel="stylesheet" href="${pageContext.request.contextPath}/plugins/adminLTE/css/AdminLTE.css"> <link rel="stylesheet" href="${pageContext.request.contextPath}/plugins/iCheck/square/blue.css"> </head> <body class="hold-transition login-page"> <div class="login-box"> <div class="login-logo"> <a href="all-admin-index.html"><b>ITCAST</b>后台管理系统</a> </div> <!-- /.login-logo --> <div class="login-box-body"> <p class="login-box-msg">登录系统</p> <form action="${pageContext.request.contextPath}/login.do" method="post"> <div class="form-group has-feedback"> <input type="text" name="username" class="form-control" placeholder="用户名"> <span class="glyphicon glyphicon-envelope form-control-feedback"></span> </div> <div class="form-group has-feedback"> <input type="password" name="password" class="form-control" placeholder="密码"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="row"> <div class="col-xs-8"> <div class="checkbox icheck"> <label><input type="checkbox"> 记住 下次自动登录</label> </div> </div> <!-- /.col --> <div class="col-xs-4"> <button type="submit" class="btn btn-primary btn-block btn-flat">登录</button> </div> <!-- /.col --> </div> </form> <a href="#">忘记密码</a><br> </div> <!-- /.login-box-body --> </div> <!-- /.login-box --> <!-- jQuery 2.2.3 --> <!-- Bootstrap 3.3.6 --> <!-- iCheck --> <script src="${pageContext.request.contextPath}/plugins/jQuery/jquery-2.2.3.min.js"></script> <script src="${pageContext.request.contextPath}/plugins/bootstrap/js/bootstrap.min.js"></script> <script src="${pageContext.request.contextPath}/plugins/iCheck/icheck.min.js"></script> <script> $(function() { $('input').iCheck({ checkboxClass : 'icheckbox_square-blue', radioClass : 'iradio_square-blue', increaseArea : '20%' // optional }); }); </script> </body> </html>
导入依赖
<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> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>${spring.security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>${spring.security.version}</version> </dependency>
配置web.xml
<!-- 配置加载类路径的配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext.xml,classpath*:spring-security.xml</param-value> </context-param>
<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>
spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:security="http://www.springframework.org/schema/security" 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.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <!-- 配置不拦截的资源 --> <security:http pattern="/login.jsp" security="none"/> <security:http pattern="/failer.jsp" security="none"/> <security:http pattern="/css/**" security="none"/> <security:http pattern="/img/**" security="none"/> <security:http pattern="/plugins/**" security="none"/> <!-- 配置具体的规则 auto-config="true" 不用自己编写登录的页面,框架提供默认登录页面 use-expressions="false" 是否使用SPEL表达式(没学习过) --> <security:http auto-config="true" use-expressions="true"> <!-- 配置具体的拦截的规则 pattern="请求路径的规则" access="访问系统的人,必须有ROLE_USER的角色" --> <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')"/> <!-- 定义跳转的具体的页面 --> <security:form-login login-page="/login.jsp" login-processing-url="/login.do" default-target-url="/index.jsp" authentication-failure-url="/failer.jsp" authentication-success-forward-url="/pages/main.jsp" /> <!-- 关闭跨域请求 --> <security:csrf disabled="true"/> <!-- 退出 --> <security:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/login.jsp" /> </security:http> <!-- 切换成数据库中的用户名和密码 --> <security:authentication-manager> <security:authentication-provider user-service-ref="userService"> <!-- 配置加密的方式 <security:password-encoder ref="passwordEncoder"/>--> <security:password-encoder ref="passwordEncoder"/> </security:authentication-provider> </security:authentication-manager> <!-- 配置加密类 --> <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> <!-- 提供了入门的方式,在内存中存入用户名和密码 <security:authentication-manager> <security:authentication-provider> <security:user-service> <security:user name="admin" password="{noop}admin" authorities="ROLE_USER"/> </security:user-service> </security:authentication-provider> </security:authentication-manager> --> <security:global-method-security pre-post-annotations="enabled" jsr250-annotations="enabled" secured-annotations="enabled"></security:global-method-security> </beans>
UserInfo
package com.itheima.ssm.domain; import java.util.List; //与数据库中users对应 public class UserInfo { private String id; private String username; private String email; private String password; private String phoneNum; private int status; private String statusStr; private List<Role> roles; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getPhoneNum() { return phoneNum; } public void setPhoneNum(String phoneNum) { this.phoneNum = phoneNum; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getStatusStr() { //状态0 未开启 1 开启 if (status == 0) { statusStr = "未开启"; } else if (status == 1) { statusStr = "开启"; } return statusStr; } public void setStatusStr(String statusStr) { this.statusStr = statusStr; } public List<Role> getRoles() { return roles; } public void setRoles(List<Role> roles) { this.roles = roles; } }
UserServiceImpl
package com.itheima.ssm.service.impl; @Service("userService") @Transactional public class UserServiceImpl implements IUserService { @Autowired private IUserDao userDao; @Autowired private BCryptPasswordEncoder bCryptPasswordEncoder; @Override public UserInfo findById(String id) throws Exception { return userDao.findById(id); } @Override public void addRoleToUser(String userId, String[] roleIds) throws Exception { for(String roleId:roleIds){ userDao.addRoleToUser(userId,roleId); } } @Override public List<Role> findOtherRoles(String userid) throws Exception { return userDao.findOtherRoles(userid); } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { System.out.println(username); UserInfo userInfo = null; try { userInfo = userDao.findByUsername(username); // System.out.println(username); // System.out.println(userInfo.toString()); } catch (Exception e) { e.printStackTrace(); } //处理自己的用户对象封装成UserDetails User user = new User(userInfo.getUsername(), userInfo.getPassword(), userInfo.getStatus() == 0 ? false : true, true, true, true, getAuthority(userInfo.getRoles())); return user; } //作用就是返回一个List集合,集合中装入的是角色描述 public List<SimpleGrantedAuthority> getAuthority(List<Role> roles) { List<SimpleGrantedAuthority> list = new ArrayList<>(); for (Role role : roles) { list.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName())); } return list; } @Override public List<UserInfo> findAll() throws Exception{ //userDao.findAll(); return userDao.findAll(); } @Override public void save(UserInfo userInfo)throws Exception { userInfo.setPassword(bCryptPasswordEncoder.encode(userInfo.getPassword())); userDao.save(userInfo); } }
IUserDao
public interface IUserDao { @Select("select * from users where username=#{username}") @Results({ @Result(id = true, property = "id", column = "id"), @Result(property = "username", column = "username"), @Result(property = "email", column = "email"), @Result(property = "password", column = "password"), @Result(property = "phoneNum", column = "phoneNum"), @Result(property = "status", column = "status"), @Result(property = "roles",column = "id",javaType = java.util.List.class,many = @Many(select = "com.itheima.ssm.dao.IRoleDao.findRoleByUserId")) }) public UserInfo findByUsername(String username) throws Exception;
IRoleDao
public interface IRoleDao { //根据用户id查询出所有对应的角色 @Select("select * from role where id in (select roleId from users_role where userId=#{userId})") public List<Role> findRoleByUserId(String userId) throws Exception; }
退出
在spring-security.xml中添加一下配置
<!-- 退出 --> <security:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/login.jsp" />
<security:http auto-config="true" use-expressions="true"> <!-- 配置具体的拦截的规则 pattern="请求路径的规则" access="访问系统的人,必须有ROLE_USER的角色" --> <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')"/> <!-- 定义跳转的具体的页面 --> <security:form-login login-page="/login.jsp" login-processing-url="/login.do" default-target-url="/index.jsp" authentication-failure-url="/failer.jsp" authentication-success-forward-url="/pages/main.jsp" /> <!-- 关闭跨域请求 --> <security:csrf disabled="true"/> <!-- 退出 --> <security:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/login.jsp" /> </security:http>
在header.jsp修改
<div class="pull-right"> <a href="${pageContext.request.contextPath}/logout.do" class="btn btn-default btn-flat">注销</a> </div>