Springboot2(52)集成Security5

源碼地址

springboot2教程系列

添加依賴

<!--數據庫相關-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.12</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

配置文件

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);
}

運行程序

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章