1. 程式人生 > 其它 >Failed to convert property value of type ‘null‘ to required type ‘double‘ for property ‘balance‘

Failed to convert property value of type ‘null‘ to required type ‘double‘ for property ‘balance‘

技術標籤:Java學習資料庫spring bootmybatisshiroSpringSecurity

1、前言

之前寫過一篇SSM整合SpringSecurity,沒看過的可以看看,可以發現在SSM框架裡整合Security框架是很繁瑣的,所以很多人選擇用Shiro搭配SSM使用。而在SpringBoot中,這個情況就不一樣了,如果說是簡單使用,只需要在SpringBoot專案中加入Security的依賴就可以了,不需要寫什麼其他的東西。

2、簡單使用

首先建一個SpringBoot專案,在pom檔案中加入以下依賴:

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

為了看Security是否已被整合進SpringBoot,可以編寫一個HelloController,如下:

@Controller
public class HelloController {
    @ResponseBody
    @GetMapping("/hello")
    public String hello(){
        return "hello";
    }
}

再接下來就可以啟動專案了,在瀏覽器中訪問localhost:8080/hello,此時會看到如下頁面:
在這裡插入圖片描述
這裡使用者名稱為user,密碼會在啟動專案時列印到控制檯,登入進去即可看到hello字串。

3、進階使用

我們使用SpringSecurity不可能每次都去重新生成一個密碼,所以有以下幾種策略來對帳戶密碼進行配置。

3.1 配置檔案

在application.properties檔案中,加入以下程式碼即可自行配置SpringSecurity的帳戶密碼:

spring.security.user.name=sang
spring.security.user.password=123
spring.security.user.roles=admin

這下啟動程式後,就不會再生成一個密碼了,登入賬戶就變成了sang,密碼就變成了123,同時這個賬戶的角色是admin。

3.2 配置類

要是還是嫌配置檔案配置的東西不夠多,還可以使用配置類進行配置,首先新建一個類,比如叫MyWebSecurityConfig,這個類需要繼承WebSecurityConfigurerAdapter,其程式碼如下:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    PasswordEncoder passwordEncoder(){
        // 加密演算法
        return new BCryptPasswordEncoder(10);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 在記憶體中配置賬號密碼
        auth.inMemoryAuthentication()
                // 賬號
                .withUser("admin")
                // 密碼【此處需要將密碼加密】
                .password(passwordEncoder().encode("123"))
                // 賬號的角色
                .roles("ADMIN")
                .and()
                // 賬號
                .withUser("user")
                // 密碼【此處需要將密碼加密】
                .password(passwordEncoder().encode("123"))
                // 賬號的角色
                .roles("USER")
                ;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                // 配置admin目錄下的所有頁面都需要ADMIN角色才能訪問
                .antMatchers("/admin/**").hasRole("ADMIN")
                // 其他請求都需要驗證許可權
                .anyRequest().authenticated()
                .and()
                // 開啟表單登入
                .formLogin()
                // 登入處理URL
                .loginProcessingUrl("/login")
                // 允許登入頁和登入處理連結不經過許可權驗證
                .permitAll()
                .and()
                // 關閉csrf
                .csrf().disable()
                ;
    }

}

程式碼的解釋都在註釋裡,接下來在HelloController中新增一個函式:

@ResponseBody
@GetMapping("/admin/hello")
public String adminHello(){
    return "hello admin";
}

啟動程式,訪問localhost:8080/hello,在登入頁面嘗試登入admin和user兩個賬號,都可以訪問hello,而訪問localhost:8080/admin/hello,則會發現只有admin賬戶才可訪問,user賬戶沒有許可權。

3.3 資料庫

像這種在記憶體中配置帳戶密碼的操作,在實際中並不實用,因為不可能每加個賬戶,就該程式碼並重啟服務,所以要把資料庫也整合進去,有關於資料庫的整合,我寫過一篇SpringBoot整合Mybatis的部落格,不會的可以看一下。
首先我們需要從資料庫中將資料拿出來,那麼需要定義個實體類物件,用於接收拿到的資料,實體類有兩個,一個是User,對應賬戶,一個是Role,對應角色:
User:

public class User implements UserDetails {

    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean locked;
    private List<Role> roles;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", enabled=" + enabled +
                ", locked=" + locked +
                ", roles=" + roles +
                '}';
    }

    public User() {
    }

    public User(Integer id, String username, String password, Boolean enabled, Boolean locked, List<Role> roles) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.enabled = enabled;
        this.locked = locked;
        this.roles = roles;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for(Role role:roles){
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return !locked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    public Integer getId() {
        return id;
    }

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

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

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

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public void setLocked(Boolean locked) {
        this.locked = locked;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
}

注意User類實現了UserDetails介面,這是為了跟Security配合使用,以及User類中有個Role的列表,是為了在Security獲取使用者相關資訊時,將使用者的角色一起帶過去。
Role:

public class Role {
    private Integer id;
    private String name;
    private String nameZh;

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", nameZh='" + nameZh + '\'' +
                '}';
    }

    public Role() {
    }

    public Role(Integer id, String name, String nameZh) {
        this.id = id;
        this.name = name;
        this.nameZh = nameZh;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNameZh() {
        return nameZh;
    }

    public void setNameZh(String nameZh) {
        this.nameZh = nameZh;
    }
}

有了實體類,接下來就是Mapper,也就是Dao層,首先是UserMapper介面:

@Mapper
public interface UserMapper {
    User loadUserByUsername(String username);
    List<Role> getUserRolesByUid(Integer id);
}

其對應的UserMapper.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTC Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.alageek.study.mapper.UserMapper">
    <select id="loadUserByUsername" parameterType="string" resultType="com.alageek.study.entity.User">
        select * from user where username = #{username}
    </select>
    <select id="getUserRolesByUid" parameterType="integer" resultType="com.alageek.study.entity.Role">
        select role.* from role, user_role where user_role.uid = #{id} and user_role.rid = role.id
    </select>
</mapper>

然後在service層定義UserService:

@Service
public class UserService implements UserDetailsService {

    @Autowired
    UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userMapper.loadUserByUsername(s);
        if(user == null){
            throw new UsernameNotFoundException("賬戶不存在");
        }
        user.setRoles(userMapper.getUserRolesByUid(user.getId()));
        return user;
    }
}

與User一樣,為了配合Security使用,UserService需要實現介面UserDetailsService,其程式碼先根據使用者名稱s獲取到使用者資訊,再通過使用者id獲取到相關角色,再將組合後的使用者資訊返回。
有了Service後,還需要在Security的配置類中進行相關配置,程式碼如下:

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

在配置類中加入上述程式碼即可,就能從資料庫中獲取賬戶資料進行比對,從而登入,而註冊只需往資料庫中插入資料即可。
寫完上述資料庫相關程式碼後,測試功能跟上面一樣,寫一個Controller啟動專案登入下試試即可。

4、相關資源

5、博主的話

原始碼沒有跟部落格說的一模一樣,不過我覺得能看到這裡的小夥伴,借鑑應該是沒有問題的,加油,一起學習吧,有什麼問題請在評論說出來。