SpringSecurity - Quick Start Demo And Know What Did It Do ?

目录

一. 实现WebMvcConfigurer接口 

二. 实现GrantedAuthority接口

三. 定义实体(附带权限集合) 

四. 实现AuthenticationSuccessHandler

五. 实现UserDetailsService接口

六. 继承WebSecurityConfigurerAdapter

七. 启动类及前端页面


 

java代码以及html页面 copy from https://blog.csdn.net/vbirdbest/article/details/90145383 

其他皆为原创。

 pom.xml如下:

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
            org.springframework.boot
            spring-boot-starter-security
        

        
            org.thymeleaf.extras
            thymeleaf-extras-springsecurity5
        

        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        

首先来配置SpringMVC相关的内容。通过实现WebMvcConfigurer的addViewControllers()方法 

1. 对于WebMvcConfigurer,看下它的源码注释。如下:

/**
 * Defines callback methods to customize the Java-based configuration for
 * Spring MVC enabled via {@code @EnableWebMvc}.
 *
 * 

{@code @EnableWebMvc}-annotated configuration classes may implement * this interface to be called back and given a chance to customize the * default configuration. * * @author Rossen Stoyanchev * @author Keith Donald * @author David Syer * @since 3.1 */

是经由@EnableWebMvc,使我们设置的SpringMVC配置生效。(所以,要有一个类标注@EnableWebMvc注解,这里可以选择启动类)。通过实现WebMvcConfigurer的相关方法来自定义我们的SpringMVC配置。

2. 再来看它的addViewController方法。源码注释,如下:

       /**
	 * Configure simple automated controllers pre-configured with the response
	 * status code and/or a view to render the response body. This is useful in
	 * cases where there is no need for custom controller logic -- e.g. render a
	 * home page, perform simple site URL redirects, return a 404 status with
	 * HTML content, a 204 with no content, and more.
	 */

 其实就是表达了它的作用是建立请求的url与视图之间的映射关系。通过url去找要跳转的视图。将这种自动化的行为配置给controller。

3. 同样地,去找ViewControllerRegistry的源码。

实际上是通过它来把我上面提到的那种映射关系变成了现实。

/**
 * Assists with the registration of simple automated controllers pre-configured
 * with status code and/or a view.
 *
 * @author Rossen Stoyanchev
 * @author Keith Donald
 * @since 3.1
 */

 ViewControllerRegistry的解释是:帮助注册controller前置配置(映射关系)的自动配置。

4. 查看addViewController方法的源码:

        /**
	 * Map a view controller to the given URL path (or pattern) in order to render
	 * a response with a pre-configured status code and view.
	 * 

Patterns like {@code "/admin/**"} or {@code "/articles/{articlename:\\w+}"} * are allowed. See {@link org.springframework.util.AntPathMatcher} for more details on the * syntax. */ public ViewControllerRegistration addViewController(String urlPath) { ViewControllerRegistration registration = new ViewControllerRegistration(urlPath); registration.setApplicationContext(this.applicationContext); this.registrations.add(registration); return registration; }

将url信息放到了ViewControllerRegistration实体中,然后将这个实体放到了ViewControllerRegistration实体集合中。 应用程序上下文暂时不讲解。最后它返回了这个实体。

5.查看setViewName源码

      /**
	 * Set the view name to return. Optional.
	 * 

If not specified, the view controller will return {@code null} as the * view name in which case the configured {@link RequestToViewNameTranslator} * will select the view name. The {@code DefaultRequestToViewNameTranslator} * for example translates "/foo/bar" to "foo/bar". * @see org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator */ public void setViewName(String viewName) { this.controller.setViewName(viewName); }

这样ViewControllerRegistration同时拥有了url和view。 再底层,我记得是DefaultRequestToViewNameTranslator具体完成uri 到 view或者视图名的解析 。

一. 实现WebMvcConfigurer接口 

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/index").setViewName("index");
        registry.addViewController("/getUserList").setViewName("userList");
        registry.addViewController("/getOrderList").setViewName("orderList");
    }
}

二. 实现GrantedAuthority接口

 

@Data
@AllArgsConstructor
public class SysPermission implements GrantedAuthority {

    private Long id;

    private String name;

    private String code;

    private String url;

    private String method;

    @Override
    public String getAuthority() {
        return "ROLE_" + this.code + ":" + this.method.toUpperCase();
    }
}

先来看下注解@Data和@AllArgsConstructor

@Data是lombok的注解。@Data是@RequiredArgsConstructor、@ToString、@Getter、@Setter、@EqualsAndHashCode五个注解的集合体。

@RequiredArgsConstructor是对final修饰的、@NonNull标注的字段生成构造器。

@ToString 重写了toString方法

@Getter、@Setter 就是生成平常的get set方法

@EqualsAndHashCode 重写equals和hashcode方法。

@AllArgsConstructor为所有成员属性生成构造器。

接下来是关键代码讲解。

public interface GrantedAuthority extends Serializable {
	
	String getAuthority();
}

GrantedAuthority表示将已经授予的权限给某个实体。也就是权限的抽象概念的实体类

getAuthority方法表示返回GrantedAuthority的字符串表现形式。必须能清晰准确表示权限。依赖Access Control Manager。

且以ROLE_ 为前缀。

上面的实现类返回了ROLE_ + 权限的名称 + 访问的方法 ,继而构成了权限字符串。表示拥有对某方法的访问的某权限。

三. 定义实体(附带权限集合) 

@Data
@AllArgsConstructor
public class SysUser {

    private Long id;

    private String username;

    private String password;

    private List sysPermissions;
}

 username、password、sysPermissions是必要的字段。可以换成其他名字。

四. 实现AuthenticationSuccessHandler

@Slf4j
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private final ObjectMapper objectMapper;

    @Autowired
    public MyAuthenticationSuccessHandler(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        log.info("login successfully {}", objectMapper.writeValueAsString(authentication));
        httpServletResponse.sendRedirect("/index");
    }
}

 验证通过后的后续处理。

onAuthenticationSuccess方法:当用户成功验证的时候,会被调用。明显是基于事件驱动的方式。

这里,log输出了Authentication实体。并且重定向到 /index,也就是index.html。

在这个方法中的第三个参数Authentication实体。

从源码中看一下。

public interface Authentication extends Principal, Serializable {
	
	Collection getAuthorities();


	Object getCredentials();


	Object getDetails();

	
	Object getPrincipal();

	
	boolean isAuthenticated();

	
	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

Authentication是在验证过程中创建的一个接口实体。

getAuthorities:获取权限集合

getCredentials:用来证明principal是否正确。通常是个密码,也可以是跟AuthenticationManager相关联的内容。

getDetails:返回验证请求的一些额外信息。比如remoteAddress、sessionid等。

getPrincipal:返回定义的一些规范,比如username 、password 、authorities 、accountNonExpired 、 accountNonLocked、 credentialsNonExpired 、enabled

 

debug中可以看出是传入的是UsernamePasswordAuthenticationToken这个实现类。

五. 实现UserDetailsService接口

@Component
public class MyUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser sysUser = new SysUser(1L, "admin", "$2a$10$VRmgVqXDdnA0RtaEum2fw.cAIn9qCIMQQtNm.DzX5AAvUVtKvpNdG",
                Arrays.asList(
                        new SysPermission(1L, "用户列表", "user:view", "/getUserList", "GET"),
                        new SysPermission(2L, "添加用户", "user:add", "/addUser", "POST"),
                        new SysPermission(3L, "修改用户", "user:update", "/updateUser", "PUT")
                ));
        if (sysUser == null) {
            throw new UsernameNotFoundException(username);
        }
        return new User(sysUser.getUsername(), sysUser.getPassword(), sysUser.getSysPermissions());
    }

    public static void main(String[] args) {
        System.out.println(new BCryptPasswordEncoder().encode("123456"));
    }
}

sysUser的创建 是模拟从数据库中 通过找到username 的实体。这里,偷懒了。

对于密码的加密采用了BCryptPasswordEncoder的encode加密方式。

接下来讲解UserDetailsService接口。

/**
 * Core interface which loads user-specific data.
 * 

* It is used throughout the framework as a user DAO and is the strategy used by the * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider * DaoAuthenticationProvider}. * *

* The interface requires only one read-only method, which simplifies support for new * data-access strategies. * * @see org.springframework.security.authentication.dao.DaoAuthenticationProvider * @see UserDetails * * @author Ben Alex */ public interface UserDetailsService { // ~ Methods // ======================================================================================================== /** * Locates the user based on the username. In the actual implementation, the search * may possibly be case sensitive, or case insensitive depending on how the * implementation instance is configured. In this case, the UserDetails * object that comes back may have a username that is of a different case than what * was actually requested.. * * @param username the username identifying the user whose data is required. * * @return a fully populated user record (never null) * * @throws UsernameNotFoundException if the user could not be found or the user has no * GrantedAuthority */ UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }

加载用户指定数据的核心接口。

通常被当做dao,是由DaoAuthenticationProvider提供的策略。

这个接口只要求一个只读方法。

再来看UserDetails的源码:

public interface UserDetails extends Serializable {
	
	Collection getAuthorities();

	
	String getPassword();

	
	String getUsername();

	
	boolean isAccountNonExpired();

	
	boolean isAccountNonLocked();


	boolean isCredentialsNonExpired();


	boolean isEnabled();
}

不再详细叙述。

接着看它的实现类User

    public User(String username, String password,
			Collection authorities) {
		this(username, password, true, true, true, true, authorities);
	}

我们这里只使用了它的一个构造器。

六. 继承WebSecurityConfigurerAdapter

 

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailsService myUserDetailsService;

    @Autowired
    private MyAuthenticationSuccessHandler successHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 禁止跨站请求伪造
        http.csrf()
                .disable()
            .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .usernameParameter("username")
                .passwordParameter("password")
                .successHandler(successHandler)
                .failureUrl("/login?error")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

七. 启动类及前端页面

@SpringBootApplication
public class Springboot2Application {

    public static void main(String[] args) {
        SpringApplication.run(Springboot2Application.class, args);
    }

}

 orderList.html




    


订单列表


userList.html




    


用户列表


index.html




    


登录名:

角色:

Username:

用户列表
添加用户
修改用户
删除用户

login.html




    
    登录


 

你可能感兴趣的:(SpringSecurity - Quick Start Demo And Know What Did It Do ?)