欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java中Spring Security的实例详解

程序员文章站 2024-02-27 17:29:15
java中spring security的实例详解 spring security是一个多方面的安全认证框架,提供了基于javaee规范的完整的安全认证解决方案。并且可以...

java中spring security的实例详解

spring security是一个多方面的安全认证框架,提供了基于javaee规范的完整的安全认证解决方案。并且可以很好与目前主流的认证框架(如cas,*授权系统)集成。使用spring security的初衷是解决不同用户登录不同应用程序的权限问题,说到权限包括两部分:认证和授权。认证是告诉系统你是谁,授权是指知道你是谁后是否有权限访问系统(授权后一般会在服务端创建一个token,之后用这个token进行后续行为的交互)。

spring security提供了多种认证模式,很多第三方的认证技术都可以很好集成:

  • form-based authentication (用于简单的用户界面)
  • openid 认证
  • authentication based on pre-established request headers (such as computer - associates siteminder)根据预先建立的请求头进行验证
  • ja-sig central authentication service ( cas, 一个开源的sso系统)
  • java authentication and authorization service (jaas)

这里只列举了部分,后面会重点介绍如何集成cas,搭建自己的认证服务。

在spring boot项目中使用spring security很容易,这里介绍如何基于内存中的用户和基于数据库进行认证。

准备

pom依赖:

<dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-security</artifactid>
        <version>1.5.1.release</version>
      </dependency>

配置:

@configuration
@enablewebsecurity
public class securityconfig extends websecurityconfigureradapter {

  @bean
  public userdetailsservice userdetailsservice() {
    return new customuserdetailsservice();
  }

  @override
  protected void configure(authenticationmanagerbuilder auth) throws exception {
    auth.inmemoryauthentication().withuser("rhwayfun").password("1209").roles("users")
        .and().withuser("admin").password("123456").roles("admin");
    //auth.jdbcauthentication().datasource(securitydatasource);
    //auth.userdetailsservice(userdetailsservice());
  }

  @override
  protected void configure(httpsecurity http) throws exception {
    http.authorizerequests()//配置安全策略
        //.antmatchers("/","/index").permitall()//定义/请求不需要验证
        .anyrequest().authenticated()//其余的所有请求都需要验证
        .and()
        .formlogin()
        .loginpage("/login")
        .defaultsuccessurl("/index")
        .permitall()
        .and()
        .logout()
        .logoutsuccessurl("/login")
        .permitall();//定义logout不需要验证

    http.csrf().disable();
  }

}

这里需要覆盖websecurityconfigureradapter的两个方法,分别定义什么请求需要什么权限,并且认证的用户密码分别是什么。

@configuration
public class webmvcconfig extends webmvcconfigureradapter {
  /**
   * 统一注册纯requestmapping跳转view的controller
   */
  @override
  public void addviewcontrollers(viewcontrollerregistry registry) {
    registry.addviewcontroller("/login").setviewname("/login");
  }
}

添加登录跳转的url,如果不加这个配置也会默认跳转到/login下,所以这里还可以自定义登录的请求路径。

登录页面:

<!doctype html>

<%--<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>--%>

<html>
<head>
  <meta charset="utf-8">
  <title>welcome springboot</title>
  <script src="/js/jquery-3.1.1.min.js"></script>
  <script src="/js/index.js"></script>
</head>
<body>

<form name="f" action="/login" method="post">
  <input id="name" name="username" type="text"/><br>
  <input id="password" name="password" type="password"><br>
  <input type="submit" value="login">
  <input name="_csrf" type="hidden" value="${_csrf}"/>
</form>

<p id="users">

</p>

<script>
  $(function () {
    $('[name=f]').focus()
  })
</script>
</body>

</html>

基于内存

securityconfig这个配置已经是基于了内存中的用户进行认证的,

auth.inmemoryauthentication()//基于内存进行认证
.withuser("rhwayfun").password("1209")//用户名密码
.roles("users")//user角色
        .and()
        .withuser("admin").password("123456").roles("admin");//admin角色

访问首页会跳转到登录页面这成功,使用配置的用户登录会跳转到首页。

基于数据库

基于数据库会复杂一些,不过原理是一致的只不过数据源从内存转到了数据库。从基于内存的例子我们大概知道spring security认证的过程:从内存查找username为输入值得用户,如果存在着验证其角色时候匹配,比如普通用户不能访问admin页面,这点可以在controller层使用@preauthorize("hasrole('role_admin')")实现,表示只有admin角色的用户才能访问该页面,spring security中角色的定义都是以role_开头,后面跟上具体的角色名称。

如果要基于数据库,可以直接指定数据源即可:

auth.jdbcauthentication().datasource(securitydatasource);

只不过数据库标是spring默认的,包括三张表:users(用户信息表)、authorities(用户角色信息表)

以下是查询用户信息以及创建用户角色的sql(部分,详细可到jdbcuserdetailsmanager类查看):

public static final string def_create_user_sql = "insert into users (username, password, enabled) values (?,?,?)";

public static final string def_insert_authority_sql = "insert into authorities (username, authority) values (?,?)";

public static final string def_user_exists_sql = "select username from users where username = ?";

那么,如果要自定义数据库表这需要配置如下,并且实现userdetailsservice接口:

auth.userdetailsservice(userdetailsservice());

@bean
  public userdetailsservice userdetailsservice() {
    return new customuserdetailsservice();
  }

customuserdetailsservice实现如下:

@service
public class customuserdetailsservice implements userdetailsservice {

  /** logger */
  private static logger log = loggerfactory.getlogger(customuserdetailsservice.class);

  @resource
  private userrepository userrepository;

  @override
  public userdetails loaduserbyusername(string username) throws usernamenotfoundexception {
    if (stringutils.isblank(username)) {
      throw new illegalargumentexception("username can't be null!");
    }
    user user;
    try {
      user = userrepository.findbyusername(username);
    } catch (exception e) {
      log.error("读取用户信息异常,", e);
      return null;
    }
    if (user == null) {
      return null;
    }
    list<userauthority> roles = userrepository.findroles(user.getuserid());
    list<simplegrantedauthority> authorities = new arraylist<>();
    for (userauthority role : roles) {
      simplegrantedauthority authority = new simplegrantedauthority(string.valueof(role.getauthid()));
      authorities.add(authority);
    }
    return new org.springframework.security.core.userdetails.user(username, "1234", authorities);
  }


}

我们需要实现loaduserbyusername方法,这里面其实级做了两件事:查询用户信息并返回该用户的角色信息。

数据库设计如下:

java中Spring Security的实例详解

数据库设计

g_users:用户基本信息表
g_authority:角色信息表
r_auth_user:用户角色信息表,这里没有使用外键约束。

使用mybatis generator生成mapper后,创建数据源securitydatasource。

@configuration
@configurationproperties(prefix = "security.datasource")
@mapperscan(basepackages = datasourceconstants.mapper_security_package, sqlsessionfactoryref = "securitysqlsessionfactory")
public class securitydatasourceconfig {

  private string url;

  private string username;

  private string password;

  private string driverclassname;

  @bean(name = "securitydatasource")
  public datasource securitydatasource() {
    druiddatasource datasource = new druiddatasource();
    datasource.setdriverclassname(driverclassname);
    datasource.seturl(url);
    datasource.setusername(username);
    datasource.setpassword(password);
    return datasource;
  }

  @bean(name = "securitytransactionmanager")
  public datasourcetransactionmanager securitytransactionmanager() {
    return new datasourcetransactionmanager(securitydatasource());
  }

  @bean(name = "securitysqlsessionfactory")
  public sqlsessionfactory securitysqlsessionfactory(@qualifier("securitydatasource") datasource securitydatasource)
      throws exception {
    final sqlsessionfactorybean sessionfactory = new sqlsessionfactorybean();
    sessionfactory.setdatasource(securitydatasource);
    sessionfactory.setmapperlocations(new pathmatchingresourcepatternresolver()
        .getresources(datasourceconstants.mapper_security_location));
    return sessionfactory.getobject();
  }

  public string geturl() {
    return url;
  }

  public void seturl(string url) {
    this.url = url;
  }

  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 string getdriverclassname() {
    return driverclassname;
  }

  public void setdriverclassname(string driverclassname) {
    this.driverclassname = driverclassname;
  }
}

那么dao userrepository就很好实现了:

public interface userrepository{

  user findbyusername(string username);

  list<userauthority> findroles(int userid);

}

在数据库插入相关数据,重启项目。仍然访问首页跳转到登录页后输入数据库插入的用户信息,如果成功跳转到首页这说明认证成功。

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!