七、Spring Boot使用Securicty完成登陆

Spring Security是一个专门针对基于Spring的项目的安全框架。

下面这些记录仅是学习中的一些记录,单篇并不完整,具体的、不明白的可以看前面的记录。

概述

在Spring Boot中,对于Security,只要在pom.xml文件中添加了依赖就会自动使用了Security,在浏览器中访问自动弹出自带的登陆界面要求输入账号密码进行登陆,用户名默认为user,密码是启动时自动生成的密码。

如果使用自己的登陆界面自己的账号密码进行登陆,需要对Security进行配置、实现AuthenticationProvider、实现UserDetailsService,同时提供UserData类、登陆页面html。

UserDetailsService主要是重写loadUserByUsername方法,通过登陆页面传入的用户名,匹配内存固化的用户名密码,或者匹配数据库里的用户名密码,加上权限填充到UserData,返回给AuthenticationProvider使用。

AuthenticationProvider可以不用,通过默认的方式进行处理。这里是使用AuthenticationProvider。主要是重写authenticate方法,获取到UserDetailsService传递过来的用户密码和登陆输入的用户名和密码,两者进行匹配认证,包括加密处理,从而判断登陆成功与否。

实现

pom.xml

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

 UserData.java

package com.net.security;

import java.io.Serializable;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;

public class UserData implements Serializable, UserDetails {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    private String username;
    private String password;
    private String role;
    private boolean accountNonExpired;
    private boolean accountNonLocked;
    private boolean credentialsNonExpired;
    private boolean enabled;
    public UserData(String username, String password, String role, boolean accountNonExpired, 
    				boolean accountNonLocked,boolean credentialsNonExpired, boolean enabled) 
    {
          // TODO Auto-generated constructor stub
          this.username = username;
          this.password = password;
          this.role = role;
          this.accountNonExpired = accountNonExpired;
          this.accountNonLocked = accountNonLocked;
          this.credentialsNonExpired = credentialsNonExpired;
          this.enabled = enabled;
    }
    // 这是权限
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
          // TODO Auto-generated method stub
          return AuthorityUtils.commaSeparatedStringToAuthorityList(role);
    }
    @Override
    public String getPassword() {
          // TODO Auto-generated method stub
          return password;
    }
    @Override
    public String getUsername() {
          // TODO Auto-generated method stub
          return username;
    }
    @Override
    public boolean isAccountNonExpired() {
          // TODO Auto-generated method stub
          return accountNonExpired;
    }
    @Override
    public boolean isAccountNonLocked() {
          // TODO Auto-generated method stub
          return accountNonLocked;
    }
    @Override
    public boolean isCredentialsNonExpired() {
          // TODO Auto-generated method stub
          return credentialsNonExpired;
    }
    @Override
    public boolean isEnabled() {
          // TODO Auto-generated method stub
          return enabled;
    }
}

 SecurityUserDetailsService.java(UserDetailsService)

package com.net.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.net.domain.User;
import com.net.security.UserData;
import com.net.service.UserService;

//登陆的用户名传入这里,可以匹配固定的内存用户名和密码,也可以匹配数据库里面查询处理的用户名和密码。通过返回值传递给别人使用
@Component
public class SecurityUserDetailsService implements UserDetailsService {

    @Autowired
    @Qualifier("userService")
    private UserService userService;
	
    @Override
    public UserDetails loadUserByUsername(String username) throws    UsernameNotFoundException 
    {
        UserData userData=null;
  	
        // TODO 根据用户名,查找到对应的密码,与权限
        System.out.println("get name:" + username);
  	
        User user=userService.loginWithLoginname(username);
	if(user != null)
	{
		System.out.println("找到用户名和密码:"+ user.toString());
		//返回的用户信息如下,参数分别是:用户名,密码,用户权限
		if(user.getLoginname().equals("admin"))
		    userData=new UserData(user.getLoginname(), user.getPassword(), "ROLE_ADMIN", true,true,true,true);//ROLE_ADMIN
		else
		    userData=new UserData(user.getLoginname(), user.getPassword(), "ROLE_USER", true,true,true,true); //ROLE_USER
	}
	else
	{
		System.out.println("没有找到用户名和密码");
	} 
		
	return userData;
  }
}

SecurityAuthenticationProvider.java(AuthenticationProvider) 

package com.net.security;

import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

import com.net.security.UserData;


//认证处理。通过UserDetailsService获取到用户名和密码,这里可以获取到登陆输入的用户名和密码,两者进行匹配认证,包括加密处理,从而判断登陆成功与否
@Component
public class SecurityAuthenticationProvider implements AuthenticationProvider {
    @Autowired
    private UserDetailsService userDetailService;
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // TODO Auto-generated method stub
        String userName = authentication.getName();	// 这个获取表单输入中返回的用户名;
        String password = authentication.getCredentials().toString();	// 这个是表单中输入的密码;
          

          // 这里构建来判断用户是否存在和密码是否正确
          System.out.println("username:"+ userName);
          System.out.println("password:"+ password);
          
          
          UserData userData = (UserData) userDetailService.loadUserByUsername(userName); 
          if (userData == null) 
          {
                throw new BadCredentialsException("用户名不存在");
          }
          
          System.out.println("userData username:"+ userData.getUsername());
          System.out.println("userData password:"+ userData.getPassword());
          

          if (!userData.getPassword().equals(password)) 
          {
                System.out.println("密码不正确");
                throw new BadCredentialsException("密码不正确");
          }
          Collection<? extends GrantedAuthority> authorities = userData.getAuthorities();
          // 构建返回的用户登录成功的token
          System.out.println("登陆成功");
          return new UsernamePasswordAuthenticationToken(userData, password, authorities);
    }
    
    @Override
    public boolean supports(Class<?> authentication) {
          // TODO Auto-generated method stub
          // 这里直接改成retrun true;表示是支持这个执行
          return true;
    }
}

 SecurityConfig.java

package com.net.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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 com.net.security.SecurityAuthenticationProvider;

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    	
    	http.headers().frameOptions().disable();

        http.formLogin()                    			
        	.loginPage("/login")			//登陆界面,用于输入用户和密码。跟控制器login中返回的名称相同
        	.loginProcessingUrl("/loginProcess")	//登陆界面提交表单的action对应的名称
        	.defaultSuccessUrl("/index")		//验证成功后默认的跳转动作
        	.failureUrl("/loginFail")		//验证失败后跳转的动作
        	.permitAll()				//表示这个不需要验证登录页面/登录失败页面
                .and()
                .authorizeRequests()
                .antMatchers("/assets/*","/css/*","/image/*","/font/*","/js/*","/Widget/*").permitAll()
                .antMatchers(HttpMethod.POST).hasRole("ADMIN")
                //.antMatchers("/index").hasRole("ADMIN")
                .anyRequest().authenticated()                  
                .and()
                .csrf().disable(); 
    }
    
    
    @Override
    public void configure(WebSecurity web) throws Exception {
        //解决静态资源被拦截的问题
        web.ignoring().antMatchers("/assets/**");
        web.ignoring().antMatchers("/css/**");
        web.ignoring().antMatchers("/images/**");
        web.ignoring().antMatchers("/font/**");
        web.ignoring().antMatchers("/js/**");
        web.ignoring().antMatchers("/Widget/**");
    }
    
    @Autowired
    private SecurityAuthenticationProvider provider;//注入我们自己的AuthenticationProvider

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {  
        //从内存中获取    
        //auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("user").password(new BCryptPasswordEncoder().encode("123456")).roles("admin");  
        //从接口中获取
    	//auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
        auth.authenticationProvider(provider);

        	
    } 

}

下面是测试代码

LoginControl.java

package com.net.web;

import java.util.List;

import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;


@Controller
public class LoginControl {
	private static Logger LOGGER = LoggerFactory.getLogger(LoginControl.class);
	
	
	@RequestMapping(value="/login")
        public String login()
	{
		LOGGER.info("login");
		
                return "login";
        }

	@RequestMapping(value="/index")
	public String index(Model model)
	{
		LOGGER.info("index");
		
		Authentication auth = SecurityContextHolder.getContext().getAuthentication(); 
		LOGGER.info("当前用户:"+auth.getName());
		
		model.addAttribute("cur_user",auth.getName());
		
                return "index";
        }
}

login.html

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />     
       
	<title>登陆</title>
</head>

<body>					
	<h4>用户登陆<font color="red"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span th:text="${message}"></span></font></h4>
	<form id="loginForm" class="" action="loginProcess" method="post">
		<i>用户名</i><input name="username" type="text"  id="username"/>
		<br>
		<i>密<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>码</i><input name="password" type="password" id="password"/>
								
		<br>
		<br>		
		<label><input type="checkbox" id="remember" class="ace"><span>保存密码</span><span>&nbsp;&nbsp;</span></label>
		<button type="submit" id="login_btn">登<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>陆</button>
	</form>
	
	

</body>
</html>

<script>
  window.onload = function(){
    var oForm = document.getElementById('loginForm');
    var oUser = document.getElementById('username');
    var oPswd = document.getElementById('password');
    var oRemember = document.getElementById('remember');
    //页面初始化时,如果帐号密码cookie存在则填充
    if(getCookie('username') && getCookie('password')){
      oUser.value = getCookie('username');
      oPswd.value = getCookie('password');
      oRemember.checked = true;
    }
    //复选框勾选状态发生改变时,如果未勾选则清除cookie
    oRemember.onchange = function(){
      if(!this.checked){
        delCookie('username');
        delCookie('password');
      }
    };
    //表单提交事件触发时,如果复选框是勾选状态则保存cookie
    oForm.onsubmit = function(){
      if(remember.checked){ 
        setCookie('username',oUser.value,7); //保存帐号到cookie,有效期7天
        setCookie('password',oPswd.value,7); //保存密码到cookie,有效期7天
      }
    };
  };
  //设置cookie
  function setCookie(name,value,day){
    var date = new Date();
    date.setDate(date.getDate() + day);
    document.cookie = name + '=' + value + ';expires='+ date;
  };
  //获取cookie
  function getCookie(name){
    var reg = RegExp(name+'=([^;]+)');
    var arr = document.cookie.match(reg);
    if(arr){
      return arr[1];
    }else{
      return '';
    }
  };
  //删除cookie
  function delCookie(name){
    setCookie(name,null,-1);
  };
</script>

index.html

<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
      
<head>
<title>用户登录</title>

</head>
<body>
	<h3>登陆成功,<span th:text="${cur_user}"></span></h3>
	<div sec:authorize="hasRole('ROLE_ADMIN')">
		<p>ROLE_ADMIN</p>
	</div>
	<div sec:authorize="hasRole('ROLE_USER')">
 		<p>ROLE_USER</p>
	</div>
</body>
</html>

因为index.html使用了 sec:authorize="hasRole('ROLE_ADMIN')"进行权限控制显示,pom.xml需要添加依赖:

<!-- 没有这个依赖html页面无法使用sec标签 -->
<dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>

 运行测试

在浏览器输入http://localhost:8080/login

回车出现下面界面

 输入admin  123456登陆失败,输入root 123456登陆成功,并跳转到index下显示。

 总结

Security配置里面直接决定了运行方式,需要跟Control和html页面进行对应的匹配,否则运行出错。                       

                .loginPage("/login")                               //登陆界面,用于输入用户和密码。跟控制器login中返回的名称相同
                .loginProcessingUrl("/loginProcess")   //登陆界面提交表单的action对应的名称
                .defaultSuccessUrl("/index")                 //验证成功后默认的跳转动作
                .failureUrl("/loginFail")                            //验证失败后跳转的动作
                .permitAll()                                             //表示这个不需要验证登录页面/登录失败页面
                .and()
                .authorizeRequests()
                .antMatchers("/assets/*","/css/*","/image/*","/font/*","/js/*","/Widget/*").permitAll()
                .antMatchers(HttpMethod.POST).hasRole("ADMIN")
                //.antMatchers("/index").hasRole("ADMIN")
                .anyRequest().authenticated()                  
                .and()
                .csrf().disable(); 

    <form id="loginForm" class="" action="loginProcess" method="post">
        xxxx
    </form>

    @RequestMapping(value="/login")
    @RequestMapping(value="/index")

以上这些都是成功运行比较关键的地方。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值