1.什么是spring security?
SpringSecurity是基于spring的安全框架,用于安全访问控制。
2.spring security登录案例
1)新建一个maven的web工程,在pom.xml中添加依赖(必须确保是web工程,打包方式是war):
<properties> <spring.version>4.2.4.RELEASE</spring.version> </properties> <dependencies> <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> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>4.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>4.1.0.RELEASE</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <!-- java编译插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <!-- 指定端口 --> <port>8888</port> <!-- 请求路径 --> <path>/</path> </configuration> </plugin> </plugins> </build>
2)新建login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>自定义登录页</h3> <!--action必须使用login,因为是spring security自动生成的,提交方式必须是post--> <form action="/login" method="post"> <input type="text" name="username"><br> <input type="text" name="password"><br> <input type="submit"> </form> </body> </html>
在这个页面中,表单必须提交到login,这个是spring security自动生成的,另外提交方式必须是post方式
input输入框的name默认都是username和password,如果要修改,需要在spring-security配置中修改
3)index.html
这是首页,登录成功时显示的页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
欢迎来到spring security
</body>
</html>
4)fail.html
这是登录失败要显示的页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
用户名或密码错误
</body>
</html>
5)配置web.xml文件
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-security.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <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>
6)编写spring-security.xml文件
在resources目录下新建一个名为spring-security.xml的文件,配置如下:
<?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.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <!--设置不拦截页面--> <http pattern="/login.html" security="none"></http> <http pattern="/fail.html" security="none"></http> <!--配置页面拦截规则--> <!--use-expressions:是否启用SPEL表达式,默认是true--> <http use-expressions="false"> <!--设置访问角色的访问路径,/**表示可以访问所有资源--> <intercept-url pattern="/**" access="ROLE_USER"></intercept-url> <!--启用表单,设置自定义的登录页,登录成功跳转页,登录失败跳转页,必须加/--> <form-login login-page="/login.html" default-target-url="/index.html" authentication-failure-url="/fail.html"/> <!-- 关闭csrf (跨站请求伪造),不然会出现403错误--> <csrf disabled="true"/> </http> <!--配置认证管理器--> <authentication-manager> <!--认证提供者--> <authentication-provider> <!--用户服务--> <user-service> <!--设置用户名、密码和角色--> <user name="admin" password="1234" authorities="ROLE_USER"></user> </user-service> </authentication-provider> </authentication-manager> </beans:beans>
intercept-url 表示拦截的页面,只有对应的角色用户登录后菜才可以查看
/* 表示的是该目录下的资源,只包括本级目录不包括下级目录
/** 表示的是该目录以及该目录下所有级别子目录的资源
security="none" 设置此资源不被拦截,如果没有设置登录页security="none" ,将会出现以下错误
因为登录页会被反复重定向,也需要关闭csrf。所有的资源后必须加/
csrf:跨站请求伪造
3.spring security部署到项目
1)导入spring security的依赖即可
<!-- 身份验证 --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> </dependency>
2)web.xml文件中添加的配置如上
3)spring-security.xml配置如上(设置默认的用户名和密码),需要部分修改,在csrf标签后添加:
<headers> <frame-options policy="SAMEORIGIN"/> </headers>
如果在系统中使用了框架页,需要设置框架页的策略为SAMEORIGIN。还要设置其他不需要拦截的资源。
4)登录页面的输入框的name改为username和password。
5)后台获取登录的用户名:
package com.hlt.controller; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/login") public class LoginController { @RequestMapping("getName") public Map getName(){ //把用户名添加到map中返回 String name = SecurityContextHolder.getContext().getAuthentication().getName(); Map map=new HashMap(); map.put("name",name); return map; } }
前端只需要发送请求来显示即可。
6)退出登录
在spring-security.xml的http节点中添加配置,指定退出后跳转的页面
<logout logout-success-url="/login.html"/>
退出按钮请求的路径必须是logout。
4.使用数据库的数据验证登录
1)创建一个类UserDetailsServiceImpl来实现UserDetailsService:
package com.hlt.service; import com.hlt.pojo.TbSeller; import com.hlt.sellergoods.service.SellerService; import lombok.Setter; 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 java.util.ArrayList; import java.util.List; public class UserDetailsServiceImpl implements UserDetailsService { private SellerService sellerService;
//这里需要有这个set方法 public void setSellerService(SellerService sellerService) { this.sellerService = sellerService; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //创建角色列表 List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER")); //根据用户名查询此用户信息 TbSeller seller = sellerService.findOne(username); if(seller!=null){ if(seller.getStatus().equals("1")){ //返回UserDetailsService的子类对象User,sprnig security会根据返回的结果与用户输入的信息进行比较 return new User(username,seller.getPassword(),grantedAuthorities); }else{ //返回null就说明输入的用户名或密码有误 return null; } }else{ return null; } } }
UserDetailsService是spring security中的一个接口。set的方法也不能少。
2)spring-security.xml的配置
<?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" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 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 http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!-- 设置不拦截页面 --> <http pattern="/shoplogin.html" security="none"></http> <http pattern="/register.html" security="none"></http> <http pattern="/css/**" security="none"></http> <http pattern="/img/**" security="none"></http> <http pattern="/js/**" security="none"></http> <http pattern="/plugins/**" security="none"></http> <http pattern="/seller/register.do" security="none"></http> <!-- 页面拦截规则 --> <http use-expressions="false"> <intercept-url pattern="/**" access="ROLE_USER" /> <form-login login-page="/shoplogin.html" default-target-url="/admin/index.html" authentication-failure-url="/shoplogin.html"/> <logout logout-success-url="/shoplogin.html"/> <csrf disabled="true"/> <headers> <frame-options policy="SAMEORIGIN"/> </headers> </http> <!-- 引用dubbo 服务 --> <dubbo:application name="hlt-sellergoods-web" /> <dubbo:registry address="zookeeper://47.98.191.197:2181"/> <dubbo:reference id="sellerService" interface="com.hlt.sellergoods.service.SellerService" > </dubbo:reference> <beans:bean class="com.hlt.service.UserDetailsServiceImpl" id="userDetailService"> <beans:property name="sellerService" ref="sellerService"></beans:property> </beans:bean> <beans:bean id="bcryptEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" /> <!-- 认证管理器 --> <authentication-manager> <authentication-provider user-service-ref="userDetailService"> <password-encoder ref="bcryptEncoder"></password-encoder> </authentication-provider> </authentication-manager> </beans:beans>
这里设置了密码加密。使用的是BCrypt加密算法,因此在用户注册的时候需要对密码进行加密,add的方法中添加如下代码
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String password = passwordEncoder.encode(seller.getPassword()); seller.setPassword(password);