登录认证自定义
-
创建密码加密器并放到spring容器中
package com.snail.learn.security.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration public class SecurityConfig { @Bean public PasswordEncoder getPasswordEncoder(){ return new BCryptPasswordEncoder(); } }
-
重写UserDetailService
package com.snail.learn.security.service; import cn.hutool.core.util.StrUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.AuthorityUtils; 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.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @Service public class UserDetailServiceImpl implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { // 登录校验逻辑(实际可以从数据库中查询) if(!StrUtil.equals("snail",userName)){ throw new UsernameNotFoundException("用户名不存在!"); } String pwd = passwordEncoder.encode("123"); // 构造用户信息(获取有权限的资源清单) return new User(userName,pwd, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal")); } }
-
测试使用
- 重启系统,则次登录,使用的账号、密码即为我们自定义中的账号和密码
登录页面自定义
-
编写登录页面
<!-- 登录处理请求必须为/login 用户名、密码必须为:username、password --> form method="post" action="/login"> <input type="text" required="required" placeholder="用户名" name="username"></input> <input type="password" required="required" placeholder="密码" name="password"></input> <button class="but" type="submit">登录</button> </form>
-
修改配置
package com.snail.learn.security.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/login.html","/**/*.css").permitAll() // .anyRequest().authenticated() // 资源拦截,并放行不需要认证的资源,如:登录页面,静态资源等 .and() .formLogin().loginPage("/login.html").loginProcessingUrl("/login") .successForwardUrl("/test") // 修改登录页面的配置:loginPage 指定登录的页面,loginProcessingUrl指定处理登录请求的url,必须也登录页面中指定的一样,successForwardUrl 指定登录成功后的跳转页面。 .and() .csrf().disable(); // 关闭csrf(暂时关闭) } @Bean public PasswordEncoder getPasswordEncoder(){ return new BCryptPasswordEncoder(); } }
-
测试使用: 再次重启后可以进入到自定义的登录页面。
-
登录页面的自定义内容还有
.failureForwardUrl("/error") // 登录失败的跳转页面 .usernameParameter("userName") // 登录页面中用户名的参数名,需要与登录页面的一致 .passwordParameter("pwd") // 登录页面中密码的参数名,需要与登录页面的一致 .successHandler() // 自定义登录成功处理类 .failureHandler() // 自定义登录失败处理类 .and() .exceptionHandling() .accessDeniedHandler(myAccessDeniedHandler) // 自定义异常处理类(无权限403)
授权
.anyRequest().authenticated() 必须在授权配置的最后面。
antMatchers: ant表达式的匹配器,另外也可以指定请求的类型(默认所有的请求)
?:匹配单个字符
*:匹配0或多个字符
**:匹配0或多个目录
示例:
/a/* :匹配 /a/ 下的所有路径,不递归,如: /a/b,/a/c,但不包括/a/b/c
/a/**:匹配 /a/ 下所有路径,递归,如 :/a/b,/a/b/c,/a/b/c/d
/a/a?c:匹配/a/ 下路径中a开头,c结尾,中间按含任意字符的路径,如:/a/adc
regexMatchers : 正则表达式的匹配器 ,另外也可以指定请求的类型(默认所有的请求)
.mvcMatchers().servletPath() : mvcMatchers 中可以配置servletPath,如 有请求/xxx/test,则可以配置成.mvcMatchers(“/test”).servletPath("/xxx")
hasAuthority(“admin”) // 需要有admin的权限 才能访问
hasAnyAuthority(“admin”,“sysadmin”) //需要有 admin或 sysadmin的权限才能访问
hasRole(“admin”) / hasAnyRole(“admin”,“sysadmin”) // 需要有“ROLE_admin”的权限 / “ROLE_admin”或“ROLE_sysadmin” …
hasRole中的值不能以“ROLE_”开头.
.access(“hasRole(‘admin’)”) // 与hasRole(“admin”) 一样
.hasIpAddress(“127.0.0.1”) // 只能本机访问
.access("@myServiceImpl.hasPermission(request,authentication)") // myServiceImpl 为自定义的权限控制类(不需继承任何类),hasPermission方法返回boolean类型,为true时表示有权限,false时表示无权限
基于注解的访问控制
允许的注解:@PreAuthorize, @PostAuthorize, @Secured( 相当于hasRole ,且权限必须以“ROLE_”开头)
对应注解需要启用的话,需要在配置类中增加@EnableGlobalMethodSecurity注解,并分别设置securedEnabled、prePostEnabled为true.
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//....
}
@RestController
public class TestController {
@RequestMapping("/test")
@Secured("ROLE_admin")
public String test(){
return "test";
}
}
记住登录
其底层实现是依赖于数据库的,所以需要集成数据库。
依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.3.18/security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
# 创建对应的数据库,不需要创建表
@Autowired
private DataSource dataSource;
@Autowired
private PersistentTokenRepository persistentTokenRepository;
@Autowired
private UserDetailServiceImpl userDetailService;
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.rememberMe()
.tokenRepository(persistentTokenRepository)
.tokenValiditySeconds(60) // 设置有效时间为60秒,默认为2周。
.userDetailsService(userDetailService)
}
<form method="post" action="/login">
<input type="text" required="required" placeholder="用户名" name="username"></input>
<input type="password" required="required" placeholder="密码" name="password"></input>
<input type="checkbox" name="remember-me" value="true"></input> <!-- name必须是“remember-me” -->
<button class="but" type="submit">登录</button>
</form>