Springboot2(52)集成Security5

源码地址

springboot2教程系列

添加依赖



    mysql
    mysql-connector-java
    5.1.47



    com.alibaba
    druid
    1.1.12



    org.springframework.boot
    spring-boot-starter-data-jpa




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



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

配置文件

spring:
  messages:
    basename: i18n/Messages,i18n/Pages
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource    # 配置当前要使用的数据源的操作类型
    driver-class-name: org.gjt.mm.mysql.Driver      # 配置MySQL的驱动程序类
    url: jdbc:mysql://47.106.106.53:3306/security?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8           # 数据库连接地址
    username: root                                  # 数据库用户名
    password: Rojao@123


spring.jpa.database: mysql
spring.jpa.database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
# 显示后台处理的SQL语句
spring.jpa.show-sql: true
# 自动检查实体和数据库表是否一致,如果不一致则会进行更新数据库表
spring.jpa.hibernate.ddl-auto: create
spring.jpa.open-in-view: false

Security配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)  //  启用方法级别的权限认证
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailsService myUserDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //  允许所有用户访问"/"和"/index.html"
        http.authorizeRequests()
                .antMatchers("/", "/index.html").permitAll()  //定义不需要认证就可以访问
                .antMatchers("/level1/**").hasRole("VIP1")    //需要拥有VIP1权限
                .anyRequest().authenticated()      // 其他地址的访问均需验证权限
                .and()
                //开启cookie保存用户数据
                .rememberMe()
                //设置cookie有效期
                .tokenValiditySeconds(60 * 60 * 24 * 7)
                .and()
                .formLogin()                     //  定义当需要用户登录时候,转到的登录页面
                .loginPage("/login.html")      //  登录页
                .failureUrl("/login-error.html").permitAll()
                .and()
                .logout()
                .logoutSuccessUrl("/index.html");
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
       /* auth.inMemoryAuthentication()
             .withUser("admin").password("123456").roles("USER");*/
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //静态资源忽略认证
        web.ignoring().antMatchers("/css/**");
    }

    /**
     * 配置登录验证
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
    }

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


}

通过@EnableWebSecurity注解开启Spring Security的功能 继承WebSecurityConfigurerAdapter,并重写它的方法来设置一些web安全的细节 configure(HttpSecurity http)方法,通过authorizeRequests()定义哪些URL需要被保护、哪些不需要被保护。例如以上代码指定了/和/home不需要任何认证就可以访问,其他的路径都必须通过身份验证。 通过formLogin()定义当需要用户登录时候,转到的登录页面。 configureGlobal(AuthenticationManagerBuilder auth)方法,在内存中创建了一个用户,该用户的名称为admin,密码为123456,用户角色为USER。

自定义密码解析

public class MyPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence charSequence) {
        //MD5Util.encode((String) charSequence);
        System.out.println(charSequence.toString());
        return null;
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        System.out.println(charSequence);
        System.out.println(s);
        return  s.equals(charSequence.toString());;
    }
}

指定了密码的加密方式(5.0 版本强制要求设置),因为我们数据库是明文存储的,所以明文返回即可

自定义UserDetailsService实现类

@Service
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private SysUserDao sysUserDao;

    /**
     * 授权的时候是对角色授权,认证的时候应该基于资源,而不是角色,因为资源是不变的,而用户的角色是会变的
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUserEntity userEntity = sysUserDao.findByUserName(username);
        if (null == userEntity) {
            throw new UsernameNotFoundException(username);
        }
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        List<SysRoleEntity> roleList = userEntity.getRoleList();
        if(roleList == null || roleList.size() == 0){
            return new User(userEntity.getUserName(), userEntity.getPassword(), authorities);
        }
        for (SysRoleEntity role : roleList) {
            List<SysPermissionEntity> permList = role.getPermissionEntityList();
            if(permList == null){
                continue;
            }
            for(SysPermissionEntity permission : permList){
                //添加用户的权限
                authorities.add(new SimpleGrantedAuthority(permission.getCode()));
            }
        }
        return new User(userEntity.getUserName(), userEntity.getPassword(), authorities);
    }
}

需要重写 loadUserByUsername 方法,参数是用户输入的用户名。返回值是UserDetails,这是一个接口,一般使用它的子类org.springframework.security.core.userdetails.User,它有三个参数,分别是用户名、密码和权限集。

自定义对 hasPermission()

@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
    @Override
    public boolean hasPermission(Authentication authentication, Object targetUrl, Object targetPermission) {
        // 获得loadUserByUsername()方法的结果
        User user = (User)authentication.getPrincipal();
        // 获得loadUserByUsername()中注入的角色
        Collection<GrantedAuthority> authorities = user.getAuthorities();
        for(GrantedAuthority authority : authorities){
            if(authority.getAuthority().equals(targetPermission)){
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable serializable, String s, Object o) {
        return false;
    }
}
@RestController
public class PermissionController {

    @RequestMapping("/perm")
    @PreAuthorize("hasPermission('/perm','perm')")
    public String perm(){
        return "success";
    }
}

@PreAuthorize("hasPermission('/perm','perm')")是关键,参数1指明了访问该接口需要的url,参数2指明了访问该接口需要的权限

自定义Filter

public class MyAuthenticationFilter  extends AbstractAuthenticationProcessingFilter {

    public MyAuthenticationFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
        return null;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        String userName = (String) req.getSession().getAttribute("username");
        if("test".equals(userName)){
            super.unsuccessfulAuthentication(req, res,new InsufficientAuthenticationException("输入的验证码不正确"));
        }
       // super.unsuccessfulAuthentication(req, res,new InsufficientAuthenticationException("输入的验证码不正确"));
        chain.doFilter(request, response);
    }
}

实体类

@Data
@Entity
@Table(name = "sys_user")
public class SysUserEntity {

    @Id
    @GeneratedValue
    @Column(name = "user_id")
    private Long userId;

    @Column(nullable = false,unique = true, length =  50)
    private String userName;

    @Column(nullable = false, length =  200)
    private String password;

    @ManyToMany(cascade = {CascadeType.ALL},fetch = FetchType.EAGER)
    @JoinTable(name="sys_user_role_map",joinColumns={@JoinColumn(name="user_id")},inverseJoinColumns={@JoinColumn(name="role_id")})
    List<SysRoleEntity> roleList;
}
@Data
@Entity
@Table(name = "sys_role")
public class SysRoleEntity {

    @Id
    @Column(name = "role_id")
    private Long roleId;

    @Column(nullable = false, length =  200)
    private String roleName;

    @ManyToMany(cascade = {CascadeType.ALL},fetch = FetchType.EAGER)
    @JoinTable(name="sys_role_permission_map",joinColumns={@JoinColumn(name="role_id")},inverseJoinColumns={@JoinColumn(name="perm_id")})
    List<SysPermissionEntity> permissionEntityList;

}
@Data
@Entity
@Table(name = "sys_permission")
public class SysPermissionEntity {

    @Id
    @Column(name = "perm_id")
    private Long permId;

    @Column(length = 30)
    private String code;
}

DAO类

@Repository
public interface SysUserDao  extends JpaRepository<SysUserEntity, Long> {
     SysUserEntity findByUserName(String userName);
}

运行程序

Springboot2(52)集成Security5_第1张图片

你可能感兴趣的:(springboot2)