Spring Security 入门学习--数据库认证和授权

  • 首先是使用的SpringBoot框架

   基础需要的pom以来如下,基础的springboot项目的创建就不一一赘述了。

        <!--spring web-->     
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
        <!--jpa 对数据库操作的框架 类似mybatis-->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-jpa</artifactId>
         </dependency>
        <!--页面引擎 thymeleaf模板-->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-thymeleaf</artifactId>
         </dependency>
         <!--mysql数据库驱动-->
         <dependency>
             <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
             <scope>runtime</scope>
         </dependency> 
  • 加入必须的security依赖
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

数据库连接配置文件

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://xx.xx.xx.xx:3306/springsecurity?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=user spring.datasource.password=password

#设置运行时打印sql语句 spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=update #关闭thymeleaf缓存 spring.thymeleaf.cache=false

#不用数据库认证时默认的用户名和密码
#spring.security.user.name=admin
#spring.security.user.password=1234
  • 实体类***********重要***********

  对于实体类的创建:

  1. 用户表包含权限表
  2. 用户表不包含权限表

  笔者使用的是用户表包含权限表的方式,那么不包含权限表该怎么解决呢?

    创建一个新的实体类,如SecurityUser 继承User 实现UserDetails 它包含User的内容和UserDetails的内容,对于权限的设置就不一样了

      重点注意这个方法 getAuthorities ,它来将权限交给security管理 暂时可以用Null值 在UserDetailsService中具体赋值

@Entity
public class User implements Serializable, UserDetails {

    private String username;

    private String password;

    private String role;

    public User(){}

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) { this.username = username; }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    /**
     * 指示用户的账户是否已过期。无法验证过期的账户。
     * 如果用户的账户有效(即未过期),则返回true,如果不在有效就返回false
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * 指示用户是锁定还是解锁。无法对锁定的用户进行身份验证。
     * 如果用户未被锁定,则返回true,否则返回false
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * 指示用户的凭证(密码)是否已过期。过期的凭证阻止身份验证
     * 如果用户的凭证有效(即未过期),则返回true
     * 如果不在有效(即过期),则返回false
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }


    /**
     * 指示用户是启用还是禁用。无法对禁用的用户进行身份验证
     * 如果启用了用户,则返回true,否则返回false
     */
    @Override
    public boolean isEnabled() {
        return true;
    }

    /**
     * 得到用户的权限,如果权限表和用户表是分开的,我们需要在重新定义一个实体类实现UserDetails 并且继承于User类
* 交给security的权限,放在UserDetailService进行处理
*/ @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<>(); //角色必须以ROLE_开头,如果数据库没有,则需要加上 至于角色为什么必须要以ROLE_开头 笔者没有进行深究 authorities.add(new SimpleGrantedAuthority("ROLE_"+this.role)); return authorities; } }
  •  此处就先不谈security的config配置吧,后面会提到

     我们还没有用到数据库中的数据,在此我们需要改造UserServiceImpl类,让其实现 UserDetailsService 并重写其中的 loadUserByUsername 方法,这是数据库认证的必要流程,贴代码:

@Service
public class UserServiceImpl implements UserService, UserDetailsService {
    @Autowired
    UserDao userDao;

    /**
     *  实现security认证实现的方法
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        logger.error("username:" + username);
       //option这个东西请读者自行研究,此处可以直接是一个user,optional只是用于判断空值,避开空指针异常的
        Optional<User> byUsername = userDao.findByUsername(username);
        if (!byUsername.isPresent()) {
            throw new UsernameNotFoundException("用户名不存在,请先注册后登录!");
        }else{
            //权限表和用户表分开************按照下面方式,如果不是,直接返回带有权限信息的User对象
            //查询的权限在此处可以通过数据库查询,并且赋值
            //List<GrantedAuthority> authorities=new ArrayList<>();
            //authorities.add(new SimpleGrantedAuthority("ROLE_SPITTER"))
           //新创建一个SecurityUser(自定义实现了UserDetails的类)
           //将authorites放到该对象中,并返回出去
            return byUsername.get();
        }
    }
}
  • 对于security的配置******************重要

  新创建一个配置类继承 WebSecurityConfigurerAdapter 

  需要注意的几个方法(重写的方法):

    configure(HttpSecurity http)   :     htpp请求安全处理

    configure(AuthenticationManagerBuilder auth)    :  自定义数据库权限认证

    configure(WebSecurity web)    :    WEB安全

   

@Configuration
@EnableWebSecurity         // 开启注解
@EnableGlobalMethodSecurity(prePostEnabled = true)  // 开启注解



//自定义了一个springSecurity安全框架的配置类 继承WebSecurityConfigurerAdapter,重写其中的方法configure,
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserServiceImpl userService;

 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()  //接触防止跨域请求
                .authorizeRequests()
                .antMatchers("/toLogin","/templates/**","/logout","/toIndex").permitAll()  //忽略认证的url
                .anyRequest().authenticated()  //其他的url都需要被认证才可以访问
                .and()
                .formLogin()      //允许自定义表单登录
                .loginPage("/toLogin")  //这是action地址  不能写具体页面  确定自定义登录页  自己不需要写login方法  登录是由security提供
                .loginProcessingUrl("/login")  //这是html中form中action的值 必须要对应
                .defaultSuccessUrl("/toIndex")  //默认登录成功会跳转的controller
               //关于登录失败的提示信息,请读者自行解决
                .failureForwardUrl("/login-error")
                .failureUrl("/login-error");
    }


     //密码加密,最新的security必须使用密码加密
    @Bean
    public PasswordEncoder passwordEncoder(){
        //使用BCcypt加密
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //从数据库读取的用户进行身份认证 实现了userDetailsService的类
        auth.userDetailsService(userService)
                .passwordEncoder(passwordEncoder());
    }

    //解除对静态资源的保护
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**","/templates/**");
    }

}
  • 接下来是Controller和页面
  • 必须要登录成功才可以访问其他的接口,没有通过认证会一直卡在login
@Controller
public class UserController {
    @Autowired
    UserServiceImpl userService;

    @RequestMapping("/toIndex")
    public String index(){
        return "index";
    }

    @RequestMapping("/toLogin")
    public String login(){
        return "login";
    }
    //定义需要访问的权限
    @PreAuthorize("hasAnyRole('ROLE_admin')")
    @RequestMapping("/sayHello")
    @ResponseBody
    public String sayHello(){
        return "hello";
    }
    //定义需要访问的权限
    @PreAuthorize("hasAnyAuthority('ROLE_user')")
    @RequestMapping("/sayYes")
    @ResponseBody
    public String sayYes(){
        return "yes";
    }


}

  html页面

<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <!--layui css-->
    <link rel="stylesheet" th:href="@{js/layui-v2.5.5/layui/css/layui.css}">
    <!--layui js-->
    <script th:src="@{js/layui-v2.5.5/layui/layui.js}"></script>
    <!--jquery-->
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <title>登录页面</title>
</head>
<body>
        <!--这里请注意action的值需要与loginProcessingUrl 相对应-->
        <form action="/login" method="post">
            <div class="layui-form-item">
                <label class="layui-form-label">输入框</label>
                <div class="layui-input-inline">
                    <input type="text" id="username" name="username" required  lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">密码框</label>
                <div class="layui-input-inline">
                    <input type="password"  name="password" required lay-verify="required" placeholder="请输入密码" autocomplete="off" class="layui-input">
                </div>
            </div>
            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button class="layui-btn layui-btn-warm" lay-submit lay-filter="formDemo">登录</button>
                </div>
            </div>
        </form>
</body>
</html>

第一次写博客 感觉还是有点乱,请和谐讨论,笔者也是看了很多其他的博客慢慢弄出来的,希望大家不要害怕麻烦,慢慢来,引入就不贴了,看了太多 也不知道谁是谁的

猜你喜欢

转载自www.cnblogs.com/linux0kk/p/12170814.html