首先先建立一个Spring Boot项目:

image.png

添加Spring Security配置:

	<!-- Spring Security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

添加配置文件:

/**
 * Created with IntelliJ IDEA.
 *
 * @Auther: Source
 * @Date: 2020/11/26/16:14
 * @Description:
 */
@Configuration
@EnableWebSecurity  //启动spring security过滤器链
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 该方法的作用就是代替之前的:<security:http>
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() // 授权配置
            .antMatchers("/**")     //拦截的路劲,/**:拦截所有
            .fullyAuthenticated()   //反问方式
            .and()
            .formLogin();	//表单验证
    }
}

从启项目就能看到控制台打印的密码,这个是自动生成加密后的密码

image.png

然后后打开网页:

image.png

就能进入一个Security自带的一个页面,然后输入user和刚才控制台打印的密码就可以登录了

image.png

自定义用户认证

修改Security配置文件:

@Configuration
//@EnableWebSecurity  //启动spring security过滤器链
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //    自定义用户信息
    @Override
    @Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("root").password("123").authorities("ROLE_ALL").build());
        manager.createUser(User.withUsername("admin").password("123").authorities("ROLE_ADD").build());
        return manager;
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    // 该方法的作用就是代替之前的:<security:http>
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/user/add").hasAnyAuthority("ROLE_ADD","ROLE_ALL")
                .antMatchers("/user/delete").hasAnyAuthority("ROLE_ADD","ROLE_ALL")
                .antMatchers("/user/update").hasAnyAuthority("ROLE_UPDATE","ROLE_ALL")
                .antMatchers("/user/list").hasAnyAuthority("ROLE_LIST","ROLE_ALL")
                .antMatchers("/user/**").hasAnyAuthority()  //所有/user/**的请求必须通过验证
                .anyRequest().permitAll()   //出了/user/**,其他的可以访问
                .and()
                .formLogin()
                .successForwardUrl("/login-success");   //自定义登录成功地址
    }
}

添加处理成功Controller:


/**
 * Created with IntelliJ IDEA.
 *
 * @Auther: Source
 * @Date: 2020/11/26/19:16
 * @Description:
 */
@Controller
public class loginInfo {

    @RequestMapping("/login-success")
    public String loginSuccess(){
        return "/index";
    }
}

添加列表访问Controller:

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/add")
    public String add(){
        return "user/add";
    }
    @RequestMapping("/update")
    public String update(){
        return "user/update";
    }

    @RequestMapping("/delete")
    public String delete(){
        return "user/delete";
    }

    @RequestMapping("/list")
    public String list(){
        return "user/list";
    }
}

添加完成后重新启动项目,访问项目,点击需要权限的页面,即可看到Security自带的登录页面
image.png

输入账号:root 密码:123
即可访问所有的页面

添加自定义异常处理

/**
 * Created with IntelliJ IDEA.
 *
 * @Auther: Source
 * @Date: 2020/11/26/19:48
 * @Description: 自定义异常处理
 */
@Component
public class ErrorPageConfig implements ErrorPageRegistrar {

    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED, "/401");
        ErrorPage error403Page = new ErrorPage(HttpStatus.FORBIDDEN, "/403");
        ErrorPage error405Page = new ErrorPage(HttpStatus.METHOD_NOT_ALLOWED, "/405");
        ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404");
        ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500");

        registry.addErrorPages(error401Page,error405Page, error404Page, error500Page, error403Page);
    }
}

在Controller添加请求:

@RequestMapping("/403")
    public String FORBIDDEN(){
        return "/403";
    }

    @RequestMapping("/404")
    public String NOT_FOUND(){
        return "/404";
    }

    @RequestMapping("/401")
    public String UNAUTHORIZED(){
        return "/401";
    }

    @RequestMapping("/500")
    public String INTERNAL_SERVER_ERROR(){
        return "/500";
    }

    @RequestMapping("/405")
    public String METHOD_NOT_ALLOWED(){
        return "/405";
    }

自定了登录页面

	...以上省略
	.and()
                .formLogin()
                .loginPage("/login")    //添加自定义登录页面
                .successForwardUrl("/login-success")   //自定义登录成功地址
        .and()
                .csrf().disable();  //关闭跨载的功能

添加图形验证码

之前有特意写到,详细请查看Security添加图形验证码

添加持久层登录

在Dao中添加根据用户名查询所有用户接口和查询权限接口

public interface UserMapper extends Mapper<MyUser> {

    @Select("select * from sys_user where username = #{username}")
    MyUser findByUserName(String username);

}
public interface PermissionMapper extends Mapper<Permission> {

    @Select("select permission.*\n" +
            "           from\n" +
            "             sys_user user\n" +
            "             inner join sys_user_role user_role on user.id = user_role.user_id\n" +
            "             inner join sys_role_permission role_permission on user_role.role_id = role_permission.role_id\n" +
            "             inner join sys_permission permission on role_permission.perm_id = permission.id\n" +
            "             where user.username = #{username};")
    List<Permission> selectByUsername(String username);

}

创建MyUserDatilsService 实现Security的UserDatilsService的接口

@Service
public class MyUserDetailsService implements UserDetailsService {

    private Logger logger = Logger.getLogger(MyUserDetailsService.class);

    @Autowired
    private UserService userService;

    @Autowired
    private PermissionService permissionService;


    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        logger.info("当前用户:" + s );

        //根据用户名查询用户信息
        MyUser myUser = userService.findUsername(s);

        if(myUser!=null) {
            //根据用户名查询当前用户所有权限
            List<Permission> permissions = permissionService.selectByUserName(s);
            //authorities:存放所有用户权限
            List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

            for (Permission perm : permissions) {
                GrantedAuthority authority = new SimpleGrantedAuthority(perm.getPermTag());
                authorities.add(authority);
            }
            //把所有权限赋值给user
            myUser.setAuthorities(authorities);

            logger.info(myUser.toString());
        }

//        UserDetails user = User.withUsername("root").password("123").authorities("ROLE_ALL").build();
        return myUser;
    }
}

记得添加@Service的注解

然后再config中添加

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

将加密方式改成BCryptPasswordEncoder

添加remember-me记住密码

添加Bean

/**
     * 注册remember-me接口
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        // createTableOnStartup属性用于是否启动项目时创建保存token信息的数据表,这里设置为false,我们自己手动创建,首次可以选择true自动创建。
        jdbcTokenRepository.setCreateTableOnStartup(false);
        return jdbcTokenRepository;
    }

然后在http中添加

.and()
                .rememberMe()
                .tokenRepository(persistentTokenRepository()) // 配置 token 持久化仓库
                .tokenValiditySeconds(3600) // remember 过期时间,单为秒
                .userDetailsService(userDetailsService) // 处理自动登录逻辑
                .and()

记得前后需要用and连接

前端只需添加一个单选框,name需要为remember-me

记住我:<input type="checkbox" name="remember-me" value="true"><br>

重启项目,勾选记住密码单选框登录就能在cookie中看到一个remember-me的cookie

image.png

注销功能

直接在security的http配置中添加配置即可

	.logout()  //开启注销
        .logoutSuccessUrl("/index");    //注销成功后跳转地址

然后在前端添加注销按钮直接href指向/logout即可

image.png

security整合thymeleaf

首先导入需要的包

	<!-- thymeleaf -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
	<!-- thymeleaf和security整合包只支持2.0.9以下的spring boot -->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity4</artifactId>
            <version>3.0.3.RELEASE</version>
        </dependency>

这里需要注意的主要是springboot的版本不能高于2.0.9
所以我这里就把版本降到了2.0.7

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.7.RELEASE</version>
    </parent>

再到配置文件中关闭thymeleaf缓存

spring:
  thymeleaf:
    cache: false

即可在项目中使用security的标签
导入时记得手动更改导入包

xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"

这里我也没用到几个标签,想了解的话去dome中看一下吧

项目下载地址:点击下载

Q.E.D.

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议