4 SpringSecurity

只需要协助SpringSecurity创建好用户对应的角色权限组,同时把各个资源所要求的权限信息设定好,剩下的像 “登录验证”、"权限验证" 等等工作都交给SpringSecurity。

4.1 权限控制的相关概念
4.2 引入依赖
        <!--   SpringSecurity     -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.10.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.10.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
4.3 SpringSecurity配置
①AbstractSecurityWebApplicationInitializer子类
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {

}
② WebSecurityConfigurerAdapter子类
@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter { }

配置完成之后发现SpringSecurity控制住了所有的请求和静态资源

4.4 放行首页和静态资源

layui中存放的是静态资源。

WebAppSecurityConfig

@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity security) throws Exception {
security
.authorizeRequests() // 开始授权请求
.antMatchers("/index.jsp") // 针对请求路径进行授权
.permitAll() // 可以无条件访问
.antMatchers("/layui/**") // 针对请求路径进行授权
.permitAll() // 可以无条件访问
.and()
.authorizeRequests() // 开始授权请求
.anyRequest() // 任何请求
.authenticated(); // 需要登录之后才能访问
}
}ted();
}
}

放行首页和静态资源

4.5 指定登录页面
@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity security) throws Exception {
security
.authorizeRequests() // 开始授权请求
.antMatchers("/index.jsp") // 针对请求路径进行授权
.permitAll() // 可以无条件访问
.antMatchers("/layui/**") // 针对请求路径进行授权
.permitAll() // 可以无条件访问
.and()
.authorizeRequests() // 开始授权请求
.anyRequest() // 任何请求
.authenticated() // 需要登录之后才能访问 .and()
.formLogin() // 使用表单形式登录
.loginPage("/index.jsp") // 指定登录页面
.loginProcessingUrl("/do/login.html"); // 指定提交登录表单的地址
}
}

不指定loginPage的话会使用SpringSecurity自带的登录页面。

而且登录指定的页面地址会自动进行promitAll()授权访问,如下面的代码注释掉上面的permitAll也是同样的效果

@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity security) throws Exception {
security
.authorizeRequests() // 开始授权请求
//.antMatchers("/index.jsp") // 针对请求路径进行授权
//.permitAll() // 可以无条件访问
.antMatchers("/layui/**") // 针对请求路径进行授权
.permitAll() // 可以无条件访问 .and()
.authorizeRequests() // 开始授权请求
.anyRequest() // 任何请求
.authenticated() // 需要登录之后才能访问 .and()
.formLogin() // 使用表单形式登录
.loginPage("/index.jsp") // 指定登录页面
.loginProcessingUrl("/do/login.html"); // 指定提交登录表单的地址,登录地址本身也需要放行
}
}
4.6 设置登录的用户名和密码

前端发送的登录请求要么使用SpringSecurity的默认值,要么使用定制的请求参数名。

① 在前端页面设置请求参数和请求地址
<form action="${pageContext.request.contextPath}/login" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<input type="text"
name="loginAcct" id="LAY-user-login-username" lay-verify="required"
placeholder="用户名" class="layui-input">
<input type="text"
name="userPswd" id="LAY-user-login-password"
lay-verify="required"
placeholder="密码" class="layui-input">
</form>

如上,前端设置的请求地址为/login,请求参数名为loginAcct、userPswd

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> 不能缺少

② 在SpringSecurity中进行自定义配置

WebAppSecurityConfig

@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
// 进行内存认证
builder.inMemoryAuthentication()
.withUser("tom")
.password("tom")
.roles("ADMIN")
.and()
.withUser("jerry")
.password("jerry")
.authorities("ADD", "DELETE");
} @Override
protected void configure(HttpSecurity security) throws Exception {
security
.authorizeRequests() // 开始授权请求
.antMatchers("/index.jsp") // 针对请求路径进行授权
.permitAll() // 可以无条件访问
.antMatchers("/layui/**") // 针对请求路径进行授权
.permitAll() // 可以无条件访问 .and()
.authorizeRequests() // 开始授权请求
.anyRequest() // 任何请求
.authenticated() // 需要登录之后才能访问 .and()
.formLogin() // 使用表单形式登录
.loginPage("/index.jsp") // 指定登录页面
.loginProcessingUrl("/login") // 指定提交登录表单的地址
.usernameParameter("loginAcct")
.passwordParameter("userPswd")
.successForwardUrl("/WEB-INF/views/main.jsp");
}
}

这样SpringSecurity就与前端的接口、参数对应上了

SpringSecurity中,只有通过通过用户名密码认证并且拥有角色或权限才能成为一个实体。

4.7 CSRF跨站请求伪造

当前端的代码中去掉了CSRF后,页面会报错:

<form action="${pageContext.request.contextPath}/login" method="post">
<%-- <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>--%>
<div class="layadmin-user-login-main">

这是为了防止跨站请求伪造的情况

4.8 实现退出

在SpringSecurity中自定义配置退出的请求地址退出后的地址

    @Override
protected void configure(HttpSecurity security) throws Exception {
security
.authorizeRequests() // 开始授权请求
.antMatchers("/index.jsp") // 针对请求路径进行授权
.permitAll() // 可以无条件访问
.antMatchers("/layui/**") // 针对请求路径进行授权
.permitAll() // 可以无条件访问 .and()
.authorizeRequests() // 开始授权请求
.anyRequest() // 任何请求
.authenticated() // 需要登录之后才能访问 .and()
.formLogin() // 使用表单形式登录
.loginPage("/index.jsp") // 指定登录页面
.loginProcessingUrl("/login") // 指定提交登录表单的地址
.usernameParameter("loginAcct")
.passwordParameter("userPswd")
.successForwardUrl("/WEB-INF/views/main.jsp") .and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/index.jsp"); }

在前端设置对应的请求地址 /logout

<a id="logoutAnchor" href="${pageContext.request.contextPath}/logout">退出</a>

然后发现页面退出时报错,请求的资源[/SpringSecurityPro/my/app/logout]不可用(

这是由于在开启CSRF的情况下所有的请求必须是POST的,而我们退出的请求是a标签即get所以出现这个错误。

禁用CSRF
@Override
protected void configure(HttpSecurity security) throws Exception {
security
.authorizeRequests() // 开始授权请求
.antMatchers("/index.jsp") // 针对请求路径进行授权
.permitAll() // 可以无条件访问
.antMatchers("/layui/**") // 针对请求路径进行授权
.permitAll() // 可以无条件访问 .and()
.authorizeRequests() // 开始授权请求
.anyRequest() // 任何请求
.authenticated() // 需要登录之后才能访问 .and()
.formLogin() // 使用表单形式登录
.loginPage("/index.jsp") // 指定登录页面
.loginProcessingUrl("/login") // 指定提交登录表单的地址
.usernameParameter("loginAcct")
.passwordParameter("userPswd")
.successForwardUrl("/WEB-INF/views/main.jsp") .and()
.csrf()
.disable() // 禁用CSRF以便于get请求
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/index.jsp"); }
4.9 基于角色或权限进行访问

之前的做法是用户绑定角色,然后角色绑定权限,但是在SpringSecurity中用户既可以绑定角色也可以绑定权限来进行资源访问。

@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
// 进行内存认证
builder.inMemoryAuthentication()
.withUser("tom")
.password("tom")
.roles("ADMIN", "学徒")
.and()
.withUser("jerry")
.password("jerry")
.authorities("ADD", "DELETE", "关门弟子");
} @Override
protected void configure(HttpSecurity security) throws Exception {
security
.authorizeRequests() // 开始授权请求
.antMatchers("/level1/**") // ant风格匹配需要授权的请求
.hasRole("学徒") // 为匹配的请求添加角色认证
.antMatchers("/level2/**")
.hasAuthority("关门弟子") // 为匹配的请求添加授权认证
.antMatchers("/index.jsp") // 针对请求路径进行授权
.permitAll() // 可以无条件访问
.antMatchers("/layui/**") // 针对请求路径进行授权
.permitAll() // 可以无条件访问 .and()
.authorizeRequests() // 开始授权请求
.anyRequest() // 任何请求
.authenticated() // 需要登录之后才能访问 .and()
.formLogin() // 使用表单形式登录
.loginPage("/index.jsp") // 指定登录页面
.loginProcessingUrl("/login") // 指定提交登录表单的地址
.usernameParameter("loginAcct")
.passwordParameter("userPswd")
.successForwardUrl("/WEB-INF/views/main.jsp") .and()
//.csrf()
//.disable() // 禁用CSRF以便于get请求
.logout()
.logoutUrl("/my/app/logout")
.logoutSuccessUrl("/index.jsp"); }
}

如此一来tom拥有弟子的角色可以发出/level1/开头的所有请求

jerry拥有 关门弟子 的权限,可以发出/level2开头的所有请求

SpringSecurity角色前缀“ROLE_”

首先看一下SpringSecurity在处理内存认证时添加用户角色 以及 配置请求角色时的底层逻辑:

可以发现角色的添加和认证都在前面添加了一个"ROLE_"的前缀,这是因为SpringSecurity将角色和权限一起放在了一个容器authorities里面,为了区分两者才添加了这个前缀。但是这是内存验证SpringSecurity有自动的设置,我们后面在处理数据库验证的时候就需要手动为角色添加前缀了。

4.10 自定义403权限不足页面
                .and()
.exceptionHandling() // 指定异常处理器
.accessDeniedPage("/WEB-INF/views/no_auth.jsp"); // 异常处理后前往的页面

这里以及上面的jsp请求因为在webInitializer中配置了DispatherServlet的映射地址为"/"(拦截除了jsp的请求),所以不会经过前端拦截器的页面解析器进行前后缀拼接

通过handle处理
                .and()
.exceptionHandling() // 指定异常处理器
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletRequest.setAttribute("message", "您没有资格访问这个页面");
httpServletRequest.getRequestDispatcher("/WEB-INF/views/no_auth.jsp").forward(httpServletRequest, httpServletResponse);
}
})

相当于使用了一个拦截器,可以在里面书写java代码进行响应处理

像退出等处理里面也有响应的handle可以进行后序处理

4.11 remeber me

① 在SpringSecurity对象中调用rememberMe()方法。

@Override
protected void configure(HttpSecurity security) throws Exception {
security
.authorizeRequests() // 开始授权请求
.antMatchers("/level1/**") // ant风格匹配需要授权的请求
.hasRole("学徒") // 为匹配的请求添加角色认证
.antMatchers("/level2/**")
.hasAuthority("关门弟子") // 为匹配的请求添加授权认证
.antMatchers("/index.jsp") // 针对请求路径进行授权
.permitAll() // 可以无条件访问
.antMatchers("/layui/**") // 针对请求路径进行授权
.permitAll() // 可以无条件访问 .and()
.authorizeRequests() // 开始授权请求
.anyRequest() // 任何请求
.authenticated() // 需要登录之后才能访问 .and()
.formLogin() // 使用表单形式登录
.loginPage("/index.jsp") // 指定登录页面
.loginProcessingUrl("/login") // 指定提交登录表单的地址
.usernameParameter("loginAcct")
.passwordParameter("userPswd")
.successForwardUrl("/WEB-INF/views/main.jsp") .and()
//.csrf()
//.disable() // 禁用CSRF以便于get请求
.logout()
.logoutUrl("/my/app/logout")
.logoutSuccessUrl("/index.jsp") .and()
.exceptionHandling() // 指定异常处理器
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletRequest.setAttribute("message", "您没有资格访问这个页面");
httpServletRequest.getRequestDispatcher("/WEB-INF/views/no_auth.jsp").forward(httpServletRequest, httpServletResponse);
}
}).
and()
.rememberMe()
; // 异常处理后前往的页面
}

② 在登录表单中携带上 remember-me 的请求参数。具体做法是将请求表单的checkbox的name设置为remember-me。

<input type="checkbox" name="remember-me" lay-skin="primary"
title="记住我"> <a href="forget.html"
class="layadmin-user-jump-change layadmin-link"
style="margin-top: 7px;">忘记密码?</a>

原理:服务器根据提交的登录表单是否携带remember-me参数返回一个期限cookie,区别于会话cookie在会话结束的时候就会被清除,期限cookie则不会,然后每次登录时只要期限cookie没有过期则不需要登录。但是这只是SpringSecurity在内存层面的登录检验,这意味着当服务器重启的时候即使客户端存在期限cookie也不能进行登录。

数据库层面remember-me

① 将数据源注入IOC容器

@ComponentScan(value = "com.hikaru", excludeFilters = {
@ComponentScan.Filter(value = {Controller.class, RestController.class})
})
@Configuration
public class RootConfig { @Bean
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://192.168.60.100:3306/mydb?characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai");
dataSource.setPassword("root");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUsername("root"); return dataSource;
} }

这里有个地方记录一下:在@Configration注解的配置类中使用@Test做单元测试会报空指针异常,但是在使用@RunWith以及配置加载注解@ContextConfiguration的单元测试上就没有这个问题,猜测和IOC容器的加载顺序有关系,不是很清楚以后多看书

@ComponentScan(value = "com.hikaru", excludeFilters = {
@ComponentScan.Filter(value = {Controller.class, RestController.class})
})
@Configuration
public class RootConfig { @Bean
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://192.168.60.100:3306/mydb?characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai");
dataSource.setPassword("root");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUsername("root"); return dataSource;
}
@Autowired
DruidDataSource dataSource;
@Test
public void test() {
System.out.println(dataSource);
}
}

这个会报空指针

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RootConfig.class)
public class SpringSecurityTest {
@Autowired
DruidDataSource dataSource; @Test
public void test() throws SQLException {
System.out.println(dataSource.getConnection());
} }

这个则不会

② 在HttpSecurity对象上开启rememberMe的token持久化tokenRepository,并传入一个转配了数据源的JdbcTokenRepositoryImpl对象。

    @Override
protected void configure(HttpSecurity security) throws Exception {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
tokenRepository.setCreateTableOnStartup(true);
tokenRepository.initDao(); security
.authorizeRequests() // 开始授权请求
.antMatchers("/level1/**") // ant风格匹配需要授权的请求
.hasRole("学徒") // 为匹配的请求添加角色认证
.antMatchers("/level2/**")
.hasAuthority("关门弟子") // 为匹配的请求添加授权认证
.antMatchers("/index.jsp") // 针对请求路径进行授权
.permitAll() // 可以无条件访问
.antMatchers("/layui/**") // 针对请求路径进行授权
.permitAll() // 可以无条件访问 .and()
.authorizeRequests() // 开始授权请求
.anyRequest() // 任何请求
.authenticated() // 需要登录之后才能访问 .and()
.formLogin() // 使用表单形式登录
.loginPage("/index.jsp") // 指定登录页面
.loginProcessingUrl("/login") // 指定提交登录表单的地址
.usernameParameter("loginAcct")
.passwordParameter("userPswd")
.successForwardUrl("/WEB-INF/views/main.jsp") .and()
//.csrf()
//.disable() // 禁用CSRF以便于get请求
.logout()
.logoutUrl("/my/app/logout")
.logoutSuccessUrl("/index.jsp") .and()
.exceptionHandling() // 指定异常处理器
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletRequest.setAttribute("message", "您没有资格访问这个页面");
httpServletRequest.getRequestDispatcher("/WEB-INF/views/no_auth.jsp").forward(httpServletRequest, httpServletResponse);
}
}).
and()
.rememberMe()
.tokenRepository(tokenRepository)
; // 异常处理后前往的页面
}

③ 重写JdbcTokenRepositoryImpl

JdbcTokenRepositoryImpl的源码中,initDao方法用于创建数据库表,但是这个方法是protected的,因此要么手动创建一张数据库表,要么重写一下源码。

    @Override
public void initDao() {
if (this.createTableOnStartup) {
this.getJdbcTemplate().execute("create table if not exists persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null)");
} }

然后启动服务器会自动创建一张persistent_logins的表,并在选中remember时自动进行登录信息持久化,这样即使服务器重启也不影响已经登录的用户信息了。

4.12 查询数据库完成认证
SpringSecurity的默认实现

执行下面语句会使用SpringSecurity的默认数据库认证,最终会调用JdbcDao类的方法查询数据库,SpringSecurity的默认实现已经将SQL语句编写在了DAOImpl中,这就要求我们需要严格按照SQL语句设计用户、角色、权限的表结构,这显然不合理。

builder.jdbcAuthentication().usersByUsernameQuery("Tom");

自定义UserDetailService类

① 自定义UserDetailService类

@Slf4j
@Component
public class MyUserDetailService implements UserDetailsService {
@Autowired
JdbcTemplate jdbcTemplate;
/**
* 根据表单提供的用户名查询User对象,并装配角色权限信息返回UserDetails
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 从数据库查询admin对象
String sql = "select loginacct, userpswd, username, email from t_admin where loginacct = ?";
List<Admin> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Admin.class), username);
Admin admin = list.get(0);
// 给admin设置角色权限信息
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
authorities.add(new SimpleGrantedAuthority("UPDATE"));
String password = admin.getUserpswd();
log.info(password);
// 把admin对象和authorities封装到userDetails里面
return new User(username, password, authorities);
}
}

② 在AuthenticationManagerBuilder对象上开启数据库认证

    @Autowired
DruidDataSource dataSource;
@Autowired
UserDetailsService userDetailsService; @Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
// 进行内存认证
//builder.inMemoryAuthentication()
// .withUser("tom")
// .password("tom")
// .roles("ADMIN", "学徒")
// .and()
// .withUser("jerry")
// .password("jerry")
// .authorities("ADD", "DELETE", "关门弟子"); // 数据库验证
builder.userDetailsService(userDetailsService);
}
4.13 密码加密

SpringSecurity提供的密码加密接口

public interface PasswordEncoder {
// 进行密码加密
String encode(CharSequence var1);
// 检验一个明文密码是否和一个密文密码一致
boolean matches(CharSequence var1, String var2);
}

① 创建一个PasswordEncoder实现类

@Component
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
return privateEncoder(charSequence);
} @Override
public boolean matches(CharSequence charSequence, String s) {
String formPassword = privateEncoder(charSequence);
String dbPassword = s;
return Objects.equals(formPassword, dbPassword);
} private String privateEncoder(CharSequence charSequence) {
String algorithm = "MD5";
try {
// 1 创建 MessageDigest 对象
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
// 2 获取字符数组
byte[] input = ((String) charSequence).getBytes();
// 3 进行加密
byte[] output = messageDigest.digest(input);
String encoded = new BigInteger(1, output).toString(16).toUpperCase(); return encoded;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}

② 在SpringSecurity中装配并使用自定义的PasswordEncoder实现类。

@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
DruidDataSource dataSource;
@Autowired
UserDetailsService userDetailsService;
@Autowired
MyPasswordEncoder myPasswordEncoder; @Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
// 进行内存认证
//builder.inMemoryAuthentication()
// .withUser("tom")
// .password("tom")
// .roles("ADMIN", "学徒")
// .and()
// .withUser("jerry")
// .password("jerry")
// .authorities("ADD", "DELETE", "关门弟子"); // 数据库验证
builder.userDetailsService(userDetailsService).passwordEncoder(myPasswordEncoder);
}

然后进行测试发现可以登录。

4.14 带盐值的加密 BCryptPasswordEncoder

上述的md5等加密方法存在着风险:固定的明文对应着固定的暗文,虽然很难从暗文推算出明文,但是可以借助已有的明文和暗文的对应关系进行猜解:

123123 -> 4297F44B13955235245B2497399D7A93

这时候其实也可以对密码的加密做一些复杂处理,如重复一定的次数以及使用不同的进制等手段保证拿到数据库的暗文也无法知晓明文

而带盐值的加密BCryptPasswordEncoder会每次针对同一个明文产生不同的密文,且不同的密文都能和明文相对应,BCryptPasswordEncoder同样也像上面自定义的MyPasswordEncoder一样继承自PasswordEncoder。

① 使BCryptPasswordEncoder类加入IOC容器并自动装配

@Configuration
@EnableWebSecurity
@Import(BCryptPasswordEncoder.class)
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
DruidDataSource dataSource;
@Autowired
UserDetailsService userDetailsService;
@Autowired
MyPasswordEncoder myPasswordEncoder; @Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;

② 在SpringSecurity的配置对象AuthenticationManagerBuilder配置BCryptPasswordEncoder

    @Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
// 进行内存认证
//builder.inMemoryAuthentication()
// .withUser("tom")
// .password("tom")
// .roles("ADMIN", "学徒")
// .and()
// .withUser("jerry")
// .password("jerry")
// .authorities("ADD", "DELETE", "关门弟子"); // 数据库验证
builder.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}

【Spring】SpringSecurity的使用的更多相关文章

  1. spring中的springSecurity安全框架的环境搭建

    首先在web.xml文件中配置监听器和过滤器 <!--监听器 加载安全框架的核心配置文件到spring容器中--> <context-param> <param-name ...

  2. springmvc+spring-security+mybatis +redis +solar框架抽取

    参考文章:Spring MVC 3 深入总结: 第二章 Spring MVC入门 —— 跟开涛学SpringMVC 参考博客:http://www.cnblogs.com/liukemng/categ ...

  3. Spring Security笔记:Remember Me(下次自动登录)

    前一节学习了如何限制登录尝试次数,今天在这个基础上再增加一点新功能:Remember Me. 很多网站,比如博客园,在登录页面就有这个选项,勾选“下次自动登录”后,在一定时间段内,只要不清空浏览器Co ...

  4. spring security method security

    参考 Spring Security 官方文档 http://www.concretepage.com/spring/spring-security/preauthorize-postauthoriz ...

  5. spring security之httpSecurity使用示例

    如果在HttpSecurity中配置需要authenticate(),则如果没有登陆,或没有相关权限,则会无法访问 2017-01-02 23:39:32.027 DEBUG 10396 --- [n ...

  6. SpringSecurity 在MVC 中的简单使用(翻译的,稍加改动)

    Spring Security允许开发人员轻松地将安全功能集成到J2EE Web应用程序中,它通过Servlet过滤器实现“用户自定义”安全检查. 在本教程中,我们将向您展示如何在Spring MVC ...

  7. Spring Security 之方法级的安全管控

    默认情况下, Spring Security 并不启用方法级的安全管控. 启用方法级的管控后, 可以针对不同的方法通过注解设置不同的访问条件. Spring Security 支持三种方法级注解, 分 ...

  8. Spring 配置 web.xml (防止spring 内存溢出)

    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" " ...

  9. How to Integrate JCaptcha in Spring Security

    The repository for JCaptcha is this one: <repository> <id>sourceforge-releases</id> ...

  10. SpringSecurity的简单使用

    导入SpringSecurity坐标 在web.xml中配置过滤器 编写spring-securiy配置文件 编写自定义认证提供者 用户新增时加密密码 配置页面的login和logout 获取登录用户 ...

随机推荐

  1. vue-cli简介

    1.定义:vue-cli(俗称:vue 脚手架)是 vue 官方提供的.快速生成 vue 工程化项目的工具,提供了终端里的 vue 命令.它可以通过 vue create 快速搭建一个新项目: 特点: ...

  2. 网络存储服务ip-san搭建

                                         网络存储服务ip-san搭建 ip-san简称SAN(Storage Area Network),中文意思存储局域网络,ip- ...

  3. tcpdump 对指定pod 进行抓包分析

    tcpdump kubectl get pod -n imas imas-chabot-759bc8c6cf-bvq7m -o json 获取到pod所在的容器信息,在对应的宿主机获取卡片信息. do ...

  4. C语言中~与!的区别

    ! 是逻辑非or否定 ​ 凡是a的值不为0的,!a 就等于0: ​ 如果a的值为0,则 !a 的值为1 而~这个是 按位取反 比如 int a=2 ; 用二进制表示为00 00 00 10; 则 !a ...

  5. 搬运 nginx代理https

    oauth2-client在Nginx代理后遇到的问题和解决方案  2020-01-17 2020-05-27  TECH 30 MINUTES READ (ABOUT 4442 WORDS) OAu ...

  6. C#中冒号:使用场景

    1. 继承类.实现接口与继承构造函数 ① 继承类 /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public p ...

  7. windows系统下使用java语言,在mysql数据库中做定时数据备份、删除

    有这样一个业务需求,需要将数据归档的表每月定时备份,并且删除之前表中的数据,话不多说,直接上代码! 注意:这种方法适合数据量小,业务要求不高的场景! 项目采用SpringBoot  + MyBatis ...

  8. Git 仓库7K stars!学Java开源项目austin要多久?

    我是3y,一年CRUD经验用十年的markdown程序员‍常年被誉为职业八股文选手 开源项目消息推送平台austin仓库地址: 消息推送平台推送下发[邮件][短信][微信服务号][微信小程序][企业微 ...

  9. 利用Vue技术实现的查询所有和添加功能

    就是根据Vue本身的特性,对之前写过的JSON等进行页面代码的简化. 其中,有俩点,需要明白一下: 在body标签里面,使用div标签,将列表数据包住,并设置一个id,便于vue对其的引用 在使用vu ...

  10. MySQL学习(十)索引

    1.索引的种类 聚簇索引,非聚簇索引 主键索引,唯一索引,普通索引(前缀索引),全文索引 单值索引,复合索引 二级索引 覆盖索引 1.1 聚簇索引,非聚簇索引 参考文档: https://www.cn ...