在处理Spring安全框架时,通常可以选择Shiro或者Security,做认证授权加密等。 推荐非SpringBoot,使用Shiro,SpringBoot项目使用Security 学习网址: Security Shiro
1.SpringBoot快速装配Security
基础步骤:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
重启SpringBoot项目,访问网页,这里使用后台提供的账号密码登录
- 实现UserDetailsService
这里开始账号,密码,角色认证(对应User中参数)
@Service
public class DetailService implements UserDetailsService {
@Autowired
private PasswordEncoder pe;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return new User("xiaoming",pe.encode("666"),new ArrayList<>());
}
}
- 选择加密器
这里使用PasswordEncoder做密码加密 - 把加密器交给Spring管理 (设置配置文件@Bean)
这里使用加密算法BCryptPasswordEncoder 其他加密算法: BCryptPasswordEncoder Argon2PasswordEncoder Pbkdf2PasswordEncoder SCryptPasswordEncoder(单向加密) 加密器详细
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder getPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
密码必须加密,否则提示Encoded password does not look like BCrypt
2.认证结合数据库使用
只需4步
- 编写数据库
必须包含账号密码角色 - 整合Mybatis
- 编写Entity实体类以及Dao类
注意:由于UserDetailsService返回类型为UserDetails,因此创建的实体类需要实现UserDetails接口 实现UserDetails类重写以下方法。
private Integer id;
private String username;
private String password;
private String realName;
private Boolean enabled;
private Boolean locked;
private Boolean expired;
private Boolean credentialsExpired;
private Date createTime;
private Date loginTime;
@Override
public boolean isAccountNonExpired() {
return this.expired;
}
@Override
public boolean isAccountNonLocked() {
return this.locked;
}
@Override
public boolean isCredentialsNonExpired() {
return this.credentialsExpired;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
重写的方法可以用做封号处理等 4. 修改UserDetailsService 直接找数据库要信息就可以了
@Service
public class DetailService implements UserDetailsService {
@Autowired
private PasswordEncoder pe;
@Autowired
private SysUserDao sysUserDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = sysUserDao.selectByUsername(username);
return sysUser;
}
}
注意数据库中密码也必须是PasswordEncoder (使用同样的加密方式)加密过的
前面Security默认将所有URL拦截掉,需要登录认证才能访问URL下面我们把将公开的URL开放供所有人访问
3.给公共URL放行
1.首先开启Spring Security Web安全配置
- 在SecurityConfig中添加注解@EnableWebSecurity
- SecurityConfig类继承WebSecurityConfigurerAdapter
- 控制Spring Security是否使用调试模式(通过注解属性debug指定),缺省为false,表示缺省不使用调试模式;
- 导入 WebSecurityConfiguration,用于配置Web安全过滤器FilterChainProxy;
- 如果是Servlet 环境,导入WebMvcSecurityConfiguration;
- 如果是OAuth2环境,导入OAuth2ClientConfiguration;
- 使用注解@EnableGlobalAuthentication启用全局认证机制;
2.编辑资源拦截配置与开放URL权限
重写
configure(HttpSecurity http)
先观察原本的configure(HttpSecurity http) 方法如下。 重写放权
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/hello").permitAll()
.anyRequest().authenticated();
http.formLogin();
http.httpBasic();
}
到这里已经成功放行了 下面简单记录一下几种主要配置
URL匹配常用的几种方式
- requestMatchers() 配置一个request Mather数组,参数为RequestMatcher 对象,其match 规则自定义,需要的时候放在最前面,对需要匹配的的规则进行自定义与过滤
- antMatchers() 配置一个request Mather 的 string数组,参数为 ant 路径格式, 直接匹配url
- anyRequest 匹配任意url,无参,必须放在最后面
URL权限 permitAll() 已登录和未登录用户都能访问 authenticated() 已登录用户能访问,未登录用户不能访问 anonymous() 未登录用户能访问,已登录用户不能访问 denyAll() 已登录和未登录用户都不能访问 rememberMe() 通过“记住我”功能直接登录的用户可以访问 fullyAuthenticated() 不是通过“记住我”功能直接登录的用户可以访问
登录login配置 1.formLogin() 配置基于表单登录的认证方式 loginPage() 登录页地址,默认“/login” loginProcessingUrl 提交表单之后真正处理登录请求的地址 defaultSuccessUrl 默认跳转到 Referer 来源页面,如果 Referer 为空,没有来源页,则跳转到默认设置的页面 successForwardUrl 登录后一律跳转到指定的地址 failuerUrl登录失败之后系统转向的url,默认是loginPage + “?error” failuerHandler登录失败之后的处理器 successHandler登录成功之后的处理器 2.httpBasic() 配置Http Basic认证方式 了解httpBasic
登出logout配置 logout() 配置登出 logoutUrl 登出url,默认是/logout logoutSuccessUrl 登出成功后跳转的 url 默认是"/login?logout" logoutSuccessHandler 登出成功处理器,设置后会把logoutSuccessUrl 置为null
4.自定义登录界面
- 添加自己的登录界面
注意两个地方 - 放开权限
- 按照上面登录login配置来配置跳转跟传参地址
- 关闭csrf或者在登录表单增加csrf字段
注意:
如果这里运行访问http://localhost:8080/myLogin.html后台不报错,但是网页报 *This application has no explicit mapping for /error, so you are seeing this as a fallback. Mon May 09 17:03:35 CST 2022 There was an unexpected error (type=Not Found, status=404). No message available* 不必惊慌,有可能原因是:
- 你的Application运行类位置放错了,所有包的外面就可以了
- 可能是你的Maven没有配置或者出现了问题,重新修改个Maven配置就可以了。
5. Remember me
这个功能主要是做网页登录记住我选项达到未来一段时间不用登录的效果。 以下代码都添加在SecurityConfig配置类中。
- Spring Security提供了一个JdbcTokenRepositoryImpl类来帮助简化Remember Me功能的开发,创建这个类并放到Spring容器中,JdbcTokenRepositoryImpl需要数据源,通过注入的方式提供。
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepositoryBean(){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
如果我们运行第二次 而jdbcTokenRepository.setCreateTableOnStartup(true);没有注释那么后台报 Error creating bean with name ‘springSecurityFilterChain’ defined in class path resource …nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table ‘persistent_logins’ already exists,意思是这个存放记住我的相关表已经创建过了。
- 使用Remember-Me自动登录时,也需要查询用户的信息,需要指定UserDetailsService。
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
- 配置token的持久化方式及有效时长
http.rememberMe().tokenRepository(persistentTokenRepositoryBean()).tokenValiditySeconds(10*24*60*60);
- 登录页面增加remember-me字段
到这里Spring Security认证部分已经基本上是完了。
提一句一般用JWT替换httpBasic,只需要Spring Security整合JWT就可以了。
|