一、添加starter
添加starter到pom.xml我们即可得到如下安全性
- 所有的Http请求路径都需要认证
- 不需要特定的角色和权限
- 没有登陆页面
- 认证过程是通过HTTP basic认证对话框实现的
- 系统只有一个用户,用户名为user
二、保证应用安全性的基本实现
- 通过登录页面来进行认证。
- 提供一个注册页面,注册完成即成为我们用户。
- 对不同的请求路径,执行不同的安全规则。比如,主页和注册页面不需要认证。
三、 SpringSecurity存储用户方式
类的注解标识:
@Configuration
@EnableWebSecurity
继承:WebSecurityConfigurerAdapter
3.1 基于内存存储用户
重写方法:configure
参数:AuthenticationManagerBuilder
应用场景:数量有限的几个用户,而且这些用户基本不会发生变化
auth
.inMemoryAuthentication()
.withUser("buzz")
.password("123456");
.authorities("ROLE_USER");
.and()
.withUser("joker")
.password("123456");
.authorities("ROLE_USER");
3.2 基于JDBC用户存储
重写方法:configure
参数:AuthenticationManagerBuilder
应用场景:用户存储与关系型数据库中
此方法不建议使用,最好使用自定义用户认证
《Spring实战》p86
3.3 以LDAP作为后端的用户存储
此方法基于spring security内嵌的LDAP服务器进行存储
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userSearchBase("ou=people")
.userSearchFilter("(uid={0})")
.groupSearchBase("ou=groups")
.groupSearchFilter("member={0}")
.passwordCompare()
.passwordEncoder((PasswordEncoder) new BCryptPasswordEncoder())
.passwordAttribute("passcode")
.and()
.contextSource()
.root("dc=tacocloud,dc=com")
.ldif("classpath:ldap.ldif");
}
3.4 自定义用户认证
此方法最适合存储与关系型数据库中。因此,需要好好学习。
自定义用户认证分为以下几个步骤实现
- 因为用来存储用户,所以要先构建用户实体
- 实现UserDetailsService接口,并实现loadUserByUsername方法。spring security框架需要根据此方法来通过数据库对用户名进行查询,然后对该用户进行安全认证和授权等操作。
- 基于第二步我们需要构造dao接口,连接数据库,进而实现查询语句。
- 配置springsecurity 添加userDetailsService服务到springsecurity配置中。
- 继续配置转码器。(PasswordEncoder)
按照上面步骤,首先构造用户实体
@Entity // spring ORM 与 数据库中表名对应
@Data // lombok 生成setter、getter
@NoArgsConstructor(access = AccessLevel.PRIVATE, force=true) // lombok 生成一个无参构造方法
@RequiredArgsConstructor // lombok 提供经过final或@NotNull修饰的没有初始化的字段的参数的构造方法,如果没有也可以无参。
public class User implements Serializable, UserDetails {
private static final long serialVersionUID = -5629829985398800755L;
@Id // jpa 主键声明
@GeneratedValue(strategy = GenerationType.AUTO) // jpa 该键策略为自增
private Long id;
private final String username;
private final String password;
private final String fullname;
private final String street;
private final String city;
private final String state;
private final String zip;
private final String phoneNumber;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
实现了UserDetails的接口,为spring security框架提供了更为详细的用户安全信息。
接着,实现UserDetailService接口
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserDAO userDAO;
public UserDetailsServiceImpl(UserDAO userDAO) {
this.userDAO = userDAO;
}
;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDAO.findByUsername(username);
if (user != null) {
return user;
}
throw new UsernameNotFoundException(
"User '" + username + "' not found");
}
}
如果存在就添加,如果不存在就抛出UsernameNotFoundException异常。
然后,配置security
@Bean // 此注解会将下面方法的返回值被注册为spring bean到spring应用上下文中
public PasswordEncoder encoder() {
return new StandardPasswordEncoder("53cr3t");
}
@Autowired
private UserDetailsService userDetailsService;
/**
* 自定义用户认证
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService) // 使用我们实现的用户详情服务作为springsecurity对数据库用户数据进行安全认证的途径。
.passwordEncoder(encoder()); // springsecurity拿到用户数据后要对密码进行转码。
}
最后,自定义登录页面,进而post表单内容到登录页面url,进行springsecurity框架的安全认证。
四、自定义登录页面
默认的,spring security会为我们提供一个对话框作为登录页面。
当我们实现了SpringSecurity配置(继承WebSecurityConfigurerAdapter)时候,会为我们提供一个默认的登录页面。
但是往往我们要使用我们自己的登录页面,因为默认的登录页面太丑了。。。
废话不多说,上代码。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login") //配置登录页面(由于登录很少controller代码,可以在webspirngmvc中配置此url对应的视图模板页面
.defaultSuccessUrl("/design", true) // 默认登录成功跳转页面,true为强制跳转
.and() // 连接,进行下一个策略配置
.logout() // 登出(清空session内容)
.logoutUrl("/") // 配置登出的跳转url
;
}