1.实战
首先我们需要新增这么个依赖,当然如果已经引用了Activiti7的话,是默认带有的
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
然后启动项目,在控制台是有一个密码的:
image.png
把这个复制好,然后再打开项目地址,就会有提示要输入账号密码,我们输入:
user 刚赋值的密码,就可以登录访问接口了。
我们再模拟一下登录:
我们实现UserDetailsService类,然后重写该方法,写死正确的密码111,然后重启,发现用户名什么都行,密码一定要是111才可以验证成功,至于为什么这里可以写死密码,可以自定debug看源码就知道了(大概就是Security可以自己拿到输入框的密码,然后与该方法传入密码对比)
@Configuration
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
String password = passwordEncoder().encode("111");
return new User(userName,password,
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ACTIVITI_USER"));
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
下面我们实现从数据库读取用户,然后可以成功登陆:
@Data
@Component
public class UserInfo implements UserDetails {
private Long id;
private String name;
private String username;
private String password;
private String address;
private String roles;
private String getAddress(){
return address;
}
/**
* Returns the authorities granted to the user. Cannot return <code>null</code>.
*
* @return the authorities, sorted by natural key (never <code>null</code>)
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.stream(roles.split(",")).map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList());
}
/**
* Returns the password used to authenticate the user.
*
* @return the password
*/
@Override
public String getPassword() {
return password;
}
/**
* Returns the username used to authenticate the user. Cannot return
* <code>null</code>.
*
* @return the username (never <code>null</code>)
*/
@Override
public String getUsername() {
return username;
}
/**
* Indicates whether the user's account has expired. An expired account cannot be
* authenticated.
*
* @return <code>true</code> if the user's account is valid (ie non-expired),
* <code>false</code> if no longer valid (ie expired)
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* Indicates whether the user is locked or unlocked. A locked user cannot be
* authenticated.
*
* @return <code>true</code> if the user is not locked, <code>false</code> otherwise
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* Indicates whether the user's credentials (password) has expired. Expired
* credentials prevent authentication.
*
* @return <code>true</code> if the user's credentials are valid (ie non-expired),
* <code>false</code> if no longer valid (ie expired)
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* Indicates whether the user is enabled or disabled. A disabled user cannot be
* authenticated.
*
* @return <code>true</code> if the user is enabled, <code>false</code> otherwise
*/
@Override
public boolean isEnabled() {
return true;
}
}
上面对应的是数据库中的user表,这个user表在本系列课程前面导入过一次,不了解的可以翻翻。
创建一个根据用户名查询用户的方法:
@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
//根据用户名查询用户
public UserInfo findByUserName(String userName){
return jdbcTemplate.queryForObject("select * from user where username='"+userName+"'",
BeanPropertyRowMapper.newInstance(UserInfo.class));
}
}
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
UserInfo userInfo = userService.findByUserName(userName);
if(userInfo==null){
throw new UsernameNotFoundException("数据库中没有此用户");
}
return userInfo;
}
这样做就是从数据库中把用户给查询出来然后验证登录了。
2.登录接口配置
首先我们需要编写登录配置信息
@Configuration
public class ActivitySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private LoginSuccessHandler loginSuccessHandler;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception{
httpSecurity.formLogin()
.loginPage("/login")
.successHandler(loginSuccessHandler)
.and()
.authorizeRequests()
.anyRequest().permitAll().and().logout().permitAll()
.and().csrf().disable().headers().frameOptions().disable();
}
}
在配置中,我们制定loginPage,这个可以指定页面或者方法,上面我们指定了方法,意思就是访问login这个接口就会走Security框架的登录验证。
successHandler 我们填写的是注入的loginSuccessHandler类,其他的都是开放登录的配置,例如anyRequest().permitAll(),.logout().permitAll()都是允许登录登出的接口访问,csrf().disable().这个是允许ajax访问,frameOptions允许前端的frame框架登录。
重写登录成功之后的方法处理
@Component("loginSuccessHandler")
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
//表单
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=UTF-8");
httpServletResponse.getWriter().write("登录成功LoginSuccessHandler");
}
//ajax
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
}
}
这个时候我们在进行ajax发送请求认证,成功。
image.png
除了登录成功的处理方法,我们还需要处理登录失败的方法:
@Component("loginFailureHandler")
public class LoginFailureHandler implements AuthenticationFailureHandler {
/**
* Called when an authentication attempt fails.
*
* @param request the request during which the authentication attempt occurred.
* @param response the response.
* @param exception the exception which was thrown to reject the authentication
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("登录失败,原因是:"+exception.getMessage());
}
}
image.png
image.png
登录失败配置好之后,登录接口就可以在失败的时候给与良好的提示信息了。
接下来,我们还需要配置一个未登录用户需要跳转的接口:
@Slf4j
@RestController
public class ActivitySecurityController {
@RequestMapping(path = "login")
@ResponseStatus(code=HttpStatus.UNAUTHORIZED)
public String requiredAuth(HttpServletRequest request, HttpServletResponse response) {
return "需要登录,请使用login.html或发起POST登录请求!";
}
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception{
httpSecurity.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.successHandler(loginSuccessHandler)
.failureHandler(loginFailureHandler)
.and()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated().and().logout().permitAll()
.and().csrf().disable().headers().frameOptions().disable();
}
这样的话,如果没有登录就会跳转到统一的页面进行提示了。
在实际开发中可以通过判断请求的来源,如果是表单的话就可以提示跳转到登录页面,如果是ajax就提示信息即可。
网友评论