iT邦幫忙

DAY 22
0

無痛學習SpringMVC與Spring Security系列 第 22

[Security] 自訂驗證(Customize Authentication)使用UserDetailsService(I)

昨天提到的驗證方式是透過Spring AuthenticationManager中的inMemoryAuthentication如下

@Override
	@Autowired
	protected void configure(AuthenticationManagerBuilder auth)
			throws Exception {
		// TODO Auto-generated method stub
		auth //Builder Design Pattern
			.inMemoryAuthentication() //自訂Runtime時的使用者帳號
				.withUser("admin") //新增user
				.password("admin12345") //指定密碼
				.roles("ADMIN", "USER") //指派權限群組
				.and() //再新增使用者
				.withUser("user")
				.password("user12345")
				.roles("USER");
	}

使用者是事先coding好的,如果我們要自己實作一個實作一個Authentication Manager中的Authentication Provider(這裡指的是inMemoryAuthentication),可以自行新增使用者,則需要用到SecurityContext, UserDetails, UserDetailService等介面,說起來還真是有點麻煩,故需要分成兩天來介紹,今天先介紹實作一個UserDetailsService負責取得UserDetails物件,UserDetails存放使用者資訊,使用者登入後放入Authentication物件中,換句話說就是Spring Security所認識的使用者,首先先建立DneUser Domain class

@Entity
public class DneUser {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int id;
	@Column(length=50)
	private String username;
	@Column(length=50)
	private String password;
	@Column(length=50)
	private boolean enabled;
	@Column(length=20)
	private String firstName;
	@Column(length=20)
	private String lastName;
	
	public int getId() {
		return id;
	}
	public void setId(int 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 boolean isEnabled() {
		return enabled;
	}
	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
}

接著建立相關的DAO class(DneUserRepository/DneUserRepositoryImp)

DneUserRepository.java
public interface DneUserRepository {
	void save(DneUser user);
	List<DneUser> findAllUsers();
	DneUser findByUserName(String username);
}





DneUserRepositoryImp.java
@Repository
@Transactional
public class DneUserRepositoryImp implements DneUserRepository {
	
	@PersistenceContext
	private EntityManager entityManager;
	
	@Override
	public void save(DneUser user) {
		// TODO Auto-generated method stub
		entityManager.persist(user);
	}
	@Override
	public List<DneUser> findAllUsers() {
		// TODO Auto-generated method stub
		Query query=entityManager.createQuery("select u from User u");
		List<DneUser> users=(List<DneUser>)query.getResultList();
		return users;
	}
	@Override
	public DneUser findByUserName(String username) {
		// TODO Auto-generated method stub
		Query query=entityManager.createQuery("select u from User u where u.username =:username");
		query.setParameter("username", username);
		return (DneUser) query.getSingleResult();
	}
}

接著建立DneUserDetailsService class實作UserDetailsService介面,這個class的目的在於取得UserDetails,使用DAO可以得到使用者名稱與密碼,Authority部分需要自己寫一個helper class用使用者名稱當作判斷來給予Authority(這只是一種方式,也可以依據其他欄位來給予Authority),其Code如下:





@Component
public class DneUserDetailsService implements UserDetailsService {
    
    @Autowired
    private DneUserRepository userRepository;
    
    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        // TODO Auto-generated method stub
        DneUser user=userRepository.findByUserName(username);
        if(user==null){
            throw new UsernameNotFoundException("Invalid username/password");
        }
        //用集合及一個helper class來取得user的擁有的role
        Collection<? extends GrantedAuthority> authorities= 
                UserAuthorityUtils.createAuthorities(user);
                //UserAuthorityUtils是helper class
        return new User(user.getUsername(), user.getPassword(), authorities); 
        //這邊的User是指org.springframework.security.core.userdetails.User
        //是UserDetails介面的實作,儲存使用者名稱、密碼及擁有權限
    }

}
helper class code如下:





public class UserAuthorityUtils {
	private static final List<GrantedAuthority> ADMIN_ROLES = AuthorityUtils
			.createAuthorityList("ROLE_ADMIN", "ROLE_USER");
			//利用Spring提供的AuthorityUtils中createAuthorityList將該群組加入相關roles
			//以便用一個List變數就儲存所有roles
	private static final List<GrantedAuthority> USER_ROLES = AuthorityUtils
			.createAuthorityList("ROLE_USER");

	public static Collection<? extends GrantedAuthority> createAuthorities(
			DneUser user) {
		String username = user.getUsername();
		if (username.contains("admin")) { //帳號中含有admin,即有管理者之role
			return ADMIN_ROLES;
		}
		return USER_ROLES; //否則則為一般使用者
	}
}

這樣就完成第一步新增UserDetails以及透過DneUserDetailService來取得UserDetails物件,明天繼續下半部,如何將UserDetails放入SpringContext以及從SpringContext取得UserDetails資訊。


上一篇
[Security]利用Spring Security Tag搭配JSTL有條件顯示網頁資訊
下一篇
[Security] 自訂驗證(Customize Authentication)使用UserDetailsService(II)
系列文
無痛學習SpringMVC與Spring Security31

尚未有邦友留言

立即登入留言