前端时间公司要做一个新的小产品,突发奇想用了spring-boot加spring-security做了登录验证,过程中总感觉晚上的资料很散,索性就整理了一下,当然我感觉我的也不全哈哈哈,基本够用。
先从最开始说起,引入maven依赖包写入pom文件:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
由于这次我使用的是spring-boot 1.3.6版本,所以所有范例都已此版本为基础。
废话不说,上源码。
现在启动类同包的地方建立spring security的配置类如:
package com.shineyue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.shineyue.security.CustomUserDetailsService;
import com.shineyue.security.ShineyueAuthenticationProvider;
/**
* 集成spring-security
* @author zml
*
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/","/login").permitAll()//1根路径和/login路径不拦截
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login") //2登陆页面
.defaultSuccessUrl("/home") //3登陆成功转向该页面
.permitAll()
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login").invalidateHttpSession(true)
.permitAll();
}
//4
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new ShineyueAuthenticationProvider(customUserDetailsService));
auth.eraseCredentials(true);
}
//5忽略静态资源的拦截
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/static/**","/informationFeedback","/js/**","/error");
}
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder(4);
}
}
其中的ShineyueAuthenticationProvider是我们处理登录信息基本逻辑的地方,代码:
package com.shineyue.security;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class ShineyueAuthenticationProvider implements AuthenticationProvider {
private UserDetailsService userDetailsService;
public ShineyueAuthenticationProvider(UserDetailsService userDetailsService){
this.userDetailsService=userDetailsService;
}
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
UserDetails userDetails =userDetailsService.loadUserByUsername(token.getName());
if(userDetails.getUsername()==null||userDetails.getUsername().equals("")){
throw new UsernameNotFoundException("用户不存在");
}else if(!userDetails.isEnabled()){
throw new DisabledException("用户已被禁用");
}
String encryptedPassword = userDetails.getPassword(); // 数据库用户的密码,一般都是加密过的
String inputPassword = (String) token.getCredentials(); // 用户输入的密码
if(!encryptedPassword.equals(inputPassword)){
throw new BadCredentialsException("密码无效,请重新输入");
}
return new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
// TODO Auto-generated method stub
return UsernamePasswordAuthenticationToken.class.equals(authentication);
}
}
customUserDetailsService对象是去数据库查询登录信息的具体实现,如代码:
package com.shineyue.security;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.annotation.Resource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.SLF4JLogFactory;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import com.shineyue.JkViewWebSocket;
import com.shineyue.calldb.CallDbException;
import com.shineyue.calldb.DbMethod;
import com.shineyue.calldb.sql.NoSuchColumnException;
import com.shineyue.calldb.sql.Row;
import com.shineyue.calldb.sql.UnsupportedConversionException;
import com.shineyue.jkjk.bean.UserInfo;
@Component
public class CustomUserDetailsService implements UserDetailsService {
private static final Log log = SLF4JLogFactory.getLog(CustomUserDetailsService.class);
/**
* arg0 登录时的用户名
*/
@Resource(name="dbTmpMethod")
private DbMethod dbTmpMethod;
@Override
public UserDetails loadUserByUsername(String arg0)
throws UsernameNotFoundException {
System.out.println("arg0"+arg0);
//在这里执行查询逻辑返回用户信息
SUser su = new SUser();
if(dbTmpMethod==null){
log.error("login error -->dbTmpMethod is null");
}else{
UserInfo ui =new UserInfo();
ui.setName(arg0);
//这下面的代码是我们自定义的数据库查询API,大家不必使用,使用我们常见的mybatis即可 将查询出的信息放入su对象中返回SecurityUser对象
try {
List<Row> l =dbTmpMethod.Open("JKJK_COMMON_GET_USERINFO", ui);
if(l.size()==1){
su.setDob(new Date());
su.setEmail("hahah@163.com");
su.setId(l.get(0).getDefInt("uid"));
su.setName(l.get(0).getTrimString("name"));
su.setPassword(l.get(0).getTrimString("password"));
su.setMianAuth(l.get(0).getTrimString("auth"));
su.setState(l.get(0).getDefInt("state"));
String[] jgqx_arr=l.get(0).getTrimString("jgqx").split(",");
if(jgqx_arr.length>0){
List<String> l_jgqx=Arrays.asList(jgqx_arr);
su.setJgqx(l_jgqx);
}
}
log.info("login user:"+l.get(0).getTrimString("name"));
} catch (CallDbException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchColumnException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedConversionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//System.out.println("dbTmpMethod is not null");
}
return new SecurityUser(su);
}
}
这3个类基本上就是完成登录的核心,当然我们还需要一些对于用户信息进行处理类。
比如:
package com.shineyue.security;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class SUser implements Serializable {
/**
*
*/
private static final long serialVersionUID = -7019867470266738718L;
private int id;
private String name;
private String email;
private String password;
private Date dob;
private String mianAuth;
private int state;
private Set<SRole> sRoles = new HashSet<SRole>(0);
private List<String> jgqx=new ArrayList<String>();
public SUser(){
}
public List<String> getJgqx() {
return jgqx;
}
public void setJgqx(List<String> jgqx) {
this.jgqx = jgqx;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getDob() {
return dob;
}
public void setDob(Date dob) {
this.dob = dob;
}
public Set<SRole> getsRoles() {
return sRoles;
}
public void setsRoles(Set<SRole> sRoles) {
this.sRoles = sRoles;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
public String getMianAuth() {
return mianAuth;
}
public void setMianAuth(String mianAuth) {
this.mianAuth = mianAuth;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
package com.shineyue.security;
public class SRole {
private int id;
private String name;
private int uid;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
}
package com.shineyue.security;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class SecurityUser implements UserDetails {
private SUser user;
/**
*
*/
private static final long serialVersionUID = -1623385418837308233L;
public SecurityUser(SUser u){
this.user=u;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
//放入用户的权限和角色信息
SimpleGrantedAuthority authority= new SimpleGrantedAuthority("auth:"+this.user.getMianAuth());
authorities.add(authority);
authorities.add(authority1);
return authorities;
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return this.user.getPassword();
}
@Override
public String getUsername() {
// TODO Auto-generated method stub
return this.user.getName();
}
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isEnabled() {
if(this.user.getState()==2){
return false;
}else{
return true;
}
}
}
然后就是在一个Controller类中写入login方法用于登录时信息的返回和状态更改具体代码如下:
@RequestMapping("login")
public String login(@RequestParam(value="error", required=false) String error,
@RequestParam(value="logout", required=false) String logout,HttpServletRequest request, HttpServletResponse response,ModelMap model){
if (error != null) {
AuthenticationException ex = (AuthenticationException)request.getSession()
.getAttribute("SPRING_SECURITY_LAST_EXCEPTION");
model.put("error", ex.getMessage());
} else if (logout != null) {
model.put("logout", "Logout successful");
}
return "login";
}
之后就是写一个登录页,别忘了等于的用户名和密码控件名词必须用username和password