什么是 Spring Security
Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。
对应的 maven 坐标:
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>5.0.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>5.0.5.RELEASE</version> </dependency>
|
Spring Security 入门案例
1、创建 maven 工程
创建 maven 工程,打包方式为 war,并在 pom.xml 导入相关的依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| <packaging>war</packaging>
<properties> <spring.version>5.0.5.RELEASE</spring.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </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>5.0.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>5.0.5.RELEASE</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <port>88</port> <path>/</path> </configuration> </plugin> </plugins> </build>
|
2、配置 web.xml
在 web.xml 中主要配置 SpringMVC 的 DispatcherServlet 和用于整合第三方框架的 DelegatingFilterProxy
DelegatingFilterProxy(代理过滤器,真正的过滤器在 spring 的配置文件),用于整合 Spring Security
DelegatingFilterProxy 是 Spring 中定义的一个 Filter 实现类,其作用是代理真正的 Filter 实现类,也就是说在调用 DelegatingFilterProxy 的 doFilter() 方法时实际上调用的是其代理 Filter 的 doFilter() 方法。其代理 Filter 必须是一个 Spring bean 对象,所以使用 DelegatingFilterProxy 的好处就是其代理 Filter 类可以使用 Spring 的依赖注入机制方便自由的使用 ApplicationContext 中的 bean。
那么 DelegatingFilterProxy 如何知道其所代理的 Filter 是哪个呢?这是通过其自身的一个叫 targetBeanName 的属性来确定的,通过该名称,DelegatingFilterProxy 可以从 WebApplicationContext 中获取指定的 bean 作为代理对象。该属性可以通过在 web.xml 中定义 DelegatingFilterProxy 时通过 init-param 来指定,如果未指定的话将默认取其在 web.xml 中声明时定义的名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<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>
<servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-security.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
</web-app>
|
在上述配置中,DelegatingFilterProxy 代理的就是名为 SpringSecurityFilterChain 的 Filter。
需要注意的是被代理的 Filter 的初始化方法 init() 和销毁方法 destroy() 默认是不会被执行的。通过设置 DelegatingFilterProxy 的 targetFilterLifecycle 属性为 true,可以使被代理 Filter 与 DelegatingFilterProxy 具有同样的生命周期。
3、 配置 spring-security.xml
在 spring-security.xml 中主要配置 Spring Security 的拦截规则和认证管理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| <?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:security="http://www.springframework.org/schema/security" 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 auto-config="true" use-expressions="true"> <security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"></security:intercept-url> </security:http>
<security:authentication-manager> <security:authentication-provider> <security:user-service> <security:user name="admin" authorities="ROLE_ADMIN" password="{noop}admin"></security:user> </security:user-service> </security:authentication-provider> </security:authentication-manager>
</beans>
|
请求 url 地址:在上面 pom.xml 文件中配置的端口:http://localhost:88/
访问该地址会自动调整到由 spring Security 提供的登录页面
之后在 webapp 文件夹下面,新建 index.html,可以正常访问 index.html
Spring Security 进阶
1、需要解决的问题
实际环境下往往有一些资源不需要认证也可以访问,也就是实现匿名访问
登录页面是由框架生成的,要设置使用自己的登录页面
直接将用户名和密码配置在了配置文件中,要将用户名和密码保存在数据库中
在配置文件中配置的密码使用明文,要实现对密码进行加密
2、配置可匿名访问的资源
在 spring-security.xml 文件中配置,指定哪些资源可以匿名访问
注意:配置信息要放在拦截规则前面,放到所有配置最前面就可以。
在这里我在 webapp 下创建了 pages 文件夹,用来存放公共资源
1
| <security:http security="none" pattern="/pages/**" />
|
3、使用指定的登录页面
在 webapp 文件夹下面,创建一个 login.html 作为项目的登录页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>登录</title> </head> <body> <form action="/login.do" method="post"> 账号:<input type="text" name="username" /><br /> 密码:<input type="password" name="password" /><br /> <input type="submit" value="submit" /> </form> </body> </html>
|
修改 spring-security.xml 文件,指定 login.html 页面可以匿名访问,否则无法访问。
1
| <security:http security="none" pattern="/login.html" />
|
继续修改 spring-security.xml 文件,加入表单登录信息的配置(配置需要放在拦截规则配置里面)
同时需要关闭 CsrfFilter 过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <security:http auto-config="false" use-expressions="true">
<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"></security:intercept-url>
<security:form-login login-page="/login.html" username-parameter="username" password-parameter="password" login-processing-url="/login.do" default-target-url="/index.html" authentication-failure-url="/login.html" always-use-default-target="true"/>
<security:csrf disabled="true"></security:csrf>
</security:http>
|
4、关于 csrf
CSRF(Cross Site Request Forgery)即跨站请求伪造。
跨站请求伪造(CSRF)是一种冒充受信任用户,向服务器发送非预期请求的攻击方式。例如,这些非预期请求可能是通过在跳转链接后的 URL 中加入恶意参数来完成。
客户端防范:对于数据库的修改请求,全部使用 POST 提交,禁止使用 GET 请求。
服务器端防范:一般的做法是在表单里面添加一段隐藏的唯一的 token(请求令牌)。
5、从数据库查询用户信息
如果我们要从数据库动态查询用户信息,就必须按照 spring security 框架的要求提供一个实现 UserDetailsService 接口的实现类,并按照框架的要求进行配置即可。框架会自动调用实现类中的方法并自动进行密码校验。
① 创建 java bean 对象
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.atguigu.pojo;
public class User { private String username; private String password; private String telephone;
|
② 实现 UserDetailsService 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| package com.atguigu.security;
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.Component;
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;
@Component public class UserService implements UserDetailsService {
static Map<String, com.atguigu.pojo.User> map = new HashMap<String, com.atguigu.pojo.User>();
static { com.atguigu.pojo.User user1 = new com.atguigu.pojo.User(); user1.setUsername("admin"); user1.setPassword("admin"); user1.setTelephone("12345");
com.atguigu.pojo.User user2 = new com.atguigu.pojo.User(); user2.setUsername("root"); user2.setPassword("root"); user2.setTelephone("54321");
map.put(user1.getUsername(), user1); map.put(user2.getUsername(), user2); }
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { com.atguigu.pojo.User user = map.get(username);
if (user == null) { return null; }
String password = user.getPassword(); List<GrantedAuthority> lists = new ArrayList<>(); lists.add(new SimpleGrantedAuthority("add")); lists.add(new SimpleGrantedAuthority("delete")); lists.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
return new User(username, password, lists); } }
|
③ 修改 spring-security.xml
配置自动扫描的包,并在 spring 配置文件中注册 UserService,指定其作为认证过程中根据用户名查询用户信息的处理类。当我们进行登录操作时,spring security 框架会调用 UserService 的 loadUserByUsername 方法查询用户信息,并根据此方法中提供的密码和用户页面输入的密码进行比对来实现认证操作。
1 2 3 4 5 6 7 8 9 10
| <context:component-scan base-package="com"/>
<security:authentication-manager> <security:authentication-provider user-service-ref="userService"> </security:authentication-provider>
</security:authentication-manager>
|
6、加密
前面我们使用的密码都是明文的,这是非常不安全的。一般情况下用户的密码需要进行加密后再保存到数据库中。
常见的密码加密方式
3DES、AES、DES:使用对称加密算法,可以通过解密来还原出原始密码
MD5、SHA1:使用单向 HASH 算法,无法通过计算还原出原始密码,但是可以建立彩虹表进行查表破解
彩虹表是一个用于加密散列函数逆运算的预先计算好的表, 为破解密码的散列值(或称哈希值、微缩图、摘要、指纹、哈希密文)而准备。一般主流的彩虹表都在 100G 以上。 这样的表常常用于恢复由有限集字符组成的固定长度的纯文本密码。这是空间/时间替换的典型实践, 比每一次尝试都计算哈希的暴力破解处理时间少而储存空间多,但却比简单的对每条输入散列翻查表的破解方式储存空间少而处理时间多。使用加 salt 的 KDF 函数可以使这种攻击难以实现。彩虹表是马丁·赫尔曼早期提出的简单算法的应用
什么是 md5 盐值?
SALT 值(盐值)属于随机值。用户注册时,系统用来和用户密码进行组合而生成的随机数值,称作 salt 值,通称为加盐值。
1、背景:系统通常把用户的密码如 MD5 加密后,以密文形式保存在数据库中,来防止黑客偷窥。
2、产生:随着对 MD5 密文查询工具的出现,而很多用户的密码又设置简单,单纯的对用户密码进行 MD5 加密后保存,用密文很容易就能反查询得到某用户的密码。
3、原理:为用户密码添加 Salt 值,使得加密的得到的密文更加冷僻,不宜查询。即使黑客有密文查询到的值,也是加了 salt 值的密码,而非用户设置的密码。salt 值是随机生成的一组字符串,可以包括随机的大小写字母、数字、字符,位数可以根据要求而不一样。
4、用途:当用户首次提供密码时(通常是注册时),由系统自动添加随机生成的 salt 值,然后再散列。而当用户登录时,系统为用户提供的代码撒上同样的加盐值,然后散列,再比较散列值,已确定密码是否正确。
5、其它:经过添加 salt 值处理的密码,即使用户设置的原密码是相通的,数据库中的密文却是不同的。
为了加强单向散列计算的安全性,会给散列算法加点盐(salt),salt 相当于加密的密钥,增加破解的难度。常用的单向散列算法有 MD5、SHA 等。
单向散列算法还有一个特点就是输入的任何微小变化都会导致输出的完全不同,这个特性有时也会被用来生成信息摘要、计算具有高离散程度的随机数等用途。
什么是 Bcrypt
Bcrypt 就是一款加密工具,可以比较方便地实现数据的加密工作。
Bcrypt 将 salt 随机并混入最终加密后的密码,验证时也无需单独提供之前的 salt,从而无需单独处理 salt 问题。你也可以简单理解为它内部自己实现了随机加盐处理
Spring Security 中的密码加密算法
spring security 中的 BCryptPasswordEncoder 方法采用 SHA-256 +随机盐+密钥对密码进行加密。SHA 系列是 Hash 算法,不是加密算法,使用加密算法意味着可以解密(这个与编码/解码一样),但是采用 Hash 处理,其过程是不可逆的。
(1)加密(encode):注册用户时,使用 SHA-256+随机盐+密钥把用户输入的密码进行 hash 处理,得到密码的 hash 值,然后将其存入数据库中。
(2)密码匹配(matches):用户登录时,密码匹配阶段并没有进行密码解密(因为密码经过 Hash 处理,是不可逆的),而是使用相同的算法把用户输入的密码进行 hash 处理,得到密码的 hash 值,然后将其与从数据库中查询到的密码 hash 值进行比较。如果两者相同,说明用户输入的密码正确。
这正是为什么处理密码时要用 hash 算法,而不用加密算法。因为这样处理即使数据库泄漏,黑客也很难破解密码。
SpringSecurity 加盐加密
加密后的格式一般为
$2a$10$cJ/afeJlWJz.88hs1Ha6tejpk0ljGF/yLxi9hTYoEwk50oHmACNWC
加密后字符串的长度为固定的 60 位。其中
$是分割符,无意义;
2a 是 bcrypt 加密版本号;
10 是 cost 的值;
而后的前 22 位是 salt 值;
再然后的字符串就是密码的密文了。
7、实现加密
① 指定密码加密对象
在 spring-security.xml 文件中指定密码加密对象
1 2 3 4 5 6 7 8 9 10
| <context:component-scan base-package="com"/> <security:authentication-manager> <security:authentication-provider user-service-ref="userService"> <security:password-encoder ref="passwordEncoder"></security:password-encoder> </security:authentication-provider> </security:authentication-manager>
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
|
② 修改 UserService 实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| package com.atguigu.security;
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.Component;
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;
@Component public class UserService implements UserDetailsService { static Map<String, com.atguigu.pojo.User> map = new HashMap<String, com.atguigu.pojo.User>();
static { com.atguigu.pojo.User user1 = new com.atguigu.pojo.User(); user1.setUsername("admin"); user1.setPassword("$2a$10$xKDLqHaeiWH04BJ495p4Ou0PA2oOo2JxBBdDJbqzww2j1ziJNnL3C"); user1.setTelephone("12345");
com.atguigu.pojo.User user2 = new com.atguigu.pojo.User(); user2.setUsername("root"); user2.setPassword("$2a$10$zKyYA3KBHLdbpuan.S4xD.b5oSIs1r7kWel3Y4wCUYSEwU.un5KoC"); user2.setTelephone("54321");
map.put(user1.getUsername(), user1); map.put(user2.getUsername(), user2); }
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { System.out.println("username" + username); com.atguigu.pojo.User userInDb = map.get(username);
if (userInDb == null) { return null; } String passwordInDb = userInDb.getPassword(); List<GrantedAuthority> lists = new ArrayList<>(); lists.add(new SimpleGrantedAuthority("add")); lists.add(new SimpleGrantedAuthority("delete")); lists.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
return new User(username, passwordInDb, lists); } }
|
③ 对页面配置多种校验规则
为了测试方便,首先在项目的 webapp 文件夹下面创建 a.html、b.html、c.html、d.html 几个页面
修改 spring-security.xml 文件,将校验规则放到拦截规则里面
前提:
- <security:http auto-config=“true” use-expressions=“true”>
- 需要关闭全局拦截
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <security:http auto-config="true" use-expressions="true">
<security:intercept-url pattern="/index.html" access="isAuthenticated()" /> <security:intercept-url pattern="/a.html" access="isAuthenticated()" />
<security:intercept-url pattern="/b.html" access="hasAuthority('add')" />
<security:intercept-url pattern="/c.html" access="hasRole('ADMIN')" />
<security:intercept-url pattern="/d.html" access="hasRole('ROLE_ROOT')" />
<security:form-login login-page="/login.html" username-parameter="username" password-parameter="password" login-processing-url="/login.do" default-target-url="/index.html" authentication-failure-url="/login.html" always-use-default-target="true"/>
<security:csrf disabled="true"></security:csrf>
</security:http>
|
④ 注解方式权限控制(对类)
Spring Security 除了可以在配置文件中配置权限校验规则,还可以使用注解方式控制类中方法的调用。例如 Controller 中的某个方法要求必须具有某个权限才可以访问,此时就可以使用 Spring Security 框架提供的注解方式进行控制。
[1]在 spring-security.xml 文件中配置组件扫描和mvc 的注解驱动,用于扫描 Controller
1 2
| <context:component-scan base-package="com"/> <mvc:annotation-driven></mvc:annotation-driven>
|
[2]在 spring-security.xml 文件中开启权限注解支持,放到最前面就可以
1 2
| <security:global-method-security pre-post-annotations="enabled" />
|
[3]创建 Controller 类并在 Controller 的方法上加入注解(@PreAuthorize)进行权限控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package com.atguigu.controller;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("/hello") public class ControllerTest {
@RequestMapping("/add") @PreAuthorize("hasAuthority('add')") public String add() { System.out.println("add..."); return "success"; }
@RequestMapping("/update") @PreAuthorize("hasRole('ROLE_ADMIN')") public String update() { System.out.println("update..."); return "success"; }
@RequestMapping("/delete") @PreAuthorize("hasRole('ROLE_ROOT')") public String delete() { System.out.println("delete..."); return "success"; } }
|
效果:使用拥有 ROLE_ADMIN 角色去访问 /hello/delete.do 没有权限
8、实现退出登录
用户完成登录后 Spring Security 框架会记录当前用户认证状态为已认证状态,即表示用户登录成功了。接下来实现用户退出登录。
①index 页面增加退出登录链接
1
| <a href="/logout.do">退出登录</a>
|
② 在 spring-security 中定义
同样需要放在拦截规则里
1 2 3 4 5 6
|
<security:logout logout-url="/logout.do" logout-success-url="/login.html" invalidate-session="true"/>
|
9、总结
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| <?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:security="http://www.springframework.org/schema/security" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 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.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<security:http security="none" pattern="/js/**"/> <security:http security="none" pattern="/css/**"/> <security:http security="none" pattern="/img/**"/> <security:http security="none" pattern="/plugins/**"/> <security:http security="none" pattern="/login.html"/>
<security:global-method-security pre-post-annotations="enabled"/>
<security:http auto-config="true" use-expressions="true">
<security:headers> <security:frame-options policy="SAMEORIGIN"></security:frame-options> </security:headers>
<security:intercept-url pattern="/index.html" access="isAuthenticated()"/> <security:intercept-url pattern="/pages/**" access="isAuthenticated()"/>
<security:form-login login-page="/login.html" username-parameter="username" password-parameter="password" login-processing-url="/login.do" default-target-url="/pages/main.html" authentication-failure-url="/login.html" always-use-default-target="true"></security:form-login> <security:logout logout-url="/logout.do" logout-success-url="/login.html" invalidate-session="true"/>
<security:csrf disabled="true"></security:csrf> </security:http>
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<security:authentication-manager>
<security:authentication-provider user-service-ref="springSecurityUserService"> <security:password-encoder ref="passwordEncoder"></security:password-encoder> </security:authentication-provider> </security:authentication-manager>
</beans>
|